def testCallMethodDirect(self): """Test whether we can call an API method directly.""" headers = client.GetAuthCredentials() config = client.GetConfigValues() url = '/'.join([ AdWordsWebServiceTestV13.SERVER, 'api/adwords/v13', 'AccountService' ]) op_config = { 'server': self.__class__.SERVER, 'version': self.__class__.VERSION, 'http_proxy': HTTP_PROXY } lock = thread.allocate_lock() service = AdWordsWebService(headers, config, op_config, url, lock) method_name = 'getAccountInfo' if config['soap_lib'] == SOAPPY: self.assert_( isinstance( service.CallMethod(method_name, (), 'AccountService'), tuple)) elif config['soap_lib'] == ZSI: web_services = __import__( 'adspygoogle.adwords.zsi.v13.AccountService_services', globals(), locals(), ['']) loc = web_services.AccountServiceLocator() request = eval('web_services.%sRequest()' % method_name) self.assert_( isinstance( service.CallMethod(method_name, (), 'Account', loc, request), tuple))
def __init__(self, headers, config, op_config, lock, logger): """Inits TrafficEstimatorService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock logger: Logger Instance of Logger """ url = [ op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__ ] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'o') if config['access']: url.insert(len(url) - 1, config['access']) self.__name_space = 'https://adwords.google.com/api/adwords' self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(TrafficEstimatorService, self).__init__(headers, config, op_config, url, 'adspygoogle.adwords', lock, logger)
class BidLandscapeService(ApiService): """Wrapper for BidLandscapeService. The BidLandscapeService service provides operations for accessing bid landscapes. """ def __init__(self, headers, config, op_config, lock, logger): """Inits BidLandscapeService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [ op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__ ] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(BidLandscapeService, self).__init__(headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def GetBidLandscape(self, selector): """Return a list of bid landscapes. Args: selector: dict Filter to run bid landscapes through. Returns: tuple List of bid landscapes meeting all the criteria of the selector. """ method_name = 'getBidLandscape' SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'BidLandscapeSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'BidLandscapeSelector') return self.__service.CallMethod(method_name, (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'BidLandscapeSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'selector': selector }, )), 'BidLandscape', self._loc, request)
class BulkOpportunityService(ApiService): """Wrapper for BulkOpportunityService. The BulkOpportunityService service lets you research opportunities for your AdWords account in bulk. """ def __init__(self, headers, config, op_config, lock, logger): """Inits BulkOpportunityService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config["server"], "api/adwords", op_config["version"], self.__class__.__name__] if AdWordsSanityCheck.IsJaxbApi(op_config["version"]): url.insert(2, "o") if config["access"]: url.insert(len(url) - 1, config["access"]) self.__service = AdWordsWebService(headers, config, op_config, "/".join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config["version"]][self.__service._GetServiceName()] super(BulkOpportunityService, self).__init__( headers, config, op_config, url, "adspygoogle.adwords", lock, logger ) def Get(self, selector): """Return a page of opportunities. Args: selector: dict Filter to run opportunities through. Returns: tuple List of user lists meeting all the criteria of the selector. """ method_name = "getBulkOpportunity" SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, "BulkOpportunitySelector") if self._config["soap_lib"] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, "selector", self._wsdl_types_map, False, "BulkOpportunitySelector" ) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split("Service")[0])[0], (selector) ) elif self._config["soap_lib"] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, "BulkOpportunitySelector", self._wsdl_types_map, self._web_services ) request = eval("self._web_services.%sRequest()" % method_name) return self.__service.CallMethod( method_name, (({"selector": selector},)), "BulkOpportunity", self._loc, request )
class GeoLocationService(ApiService): """Wrapper for GeoLocationService. The GeoLocationService service provides operations for accessing, modifying, and creating campaign ad extension. """ def __init__(self, headers, config, op_config, lock, logger): """Inits GeoLocationService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(GeoLocationService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of geo locationss. Args: selector: dict Filter to run geo locations through. Returns: tuple List of geo locations meeting all the criteria of the selector. """ method_name = 'getGeoLocation' SanityCheck.NewSanityCheck( self._wsdl_types_map, selector, 'GeoLocationSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'GeoLocationSelector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'GeoLocationSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'selector': selector},)), 'GeoLocation', self._loc, request)
def __init__(self, headers, config, op_config, lock, logger): """Inits AdGroupAdService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(AdGroupAdService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger)
def __init__(self, headers, config, op_config, lock, logger): """Inits BulkMutateJobService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ # NOTE(api.sgrinberg): Custom handling for BulkMutateJobService, whose # group in URL is 'job/' which is different from its namespace 'cm/'. url = [op_config['server'], 'api/adwords', 'job', op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(BulkMutateJobService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger)
def __init__(self, headers, config, op_config, lock, logger): """Inits ServicedAccountService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config["server"], "api/adwords", op_config["group"], op_config["version"], self.__class__.__name__] if config["access"]: url.insert(len(url) - 1, config["access"]) self.__service = AdWordsWebService(headers, config, op_config, "/".join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config["version"]][self.__service._GetServiceName()] super(ServicedAccountService, self).__init__( headers, config, op_config, url, "adspygoogle.adwords", lock, logger )
def __init__(self, headers, config, op_config, lock, logger): """Inits GeoLocationService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(GeoLocationService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger)
def __init__(self, headers, config, op_config, lock, logger): """Inits TrafficEstimatorService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'o') if config['access']: url.insert(len(url) - 1, config['access']) self.__name_space = 'https://adwords.google.com/api/adwords' self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(TrafficEstimatorService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger)
class AccountService(ApiService): """Wrapper for AccountService. The AccountService service allows you to modify AdWords accounts, such as changing targeting, business category, and email preferences for existing accounts. """ def __init__(self, headers, config, op_config, lock, logger): """Inits AccountService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(AccountService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def GetAccountInfo(self): """Return the AdWords account specified by the client account header. Returns: tuple Response from the API method. """ method_name = 'getAccountInfo' if self._config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, ()) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (), 'Account', self._loc, request) def GetClientAccountInfos(self): """Get the client account info for managed clients of effective user. Returns: tuple Response from the API method. """ method_name = 'getClientAccountInfos' if self._config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, ()) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (), 'Account', self._loc, request) def GetClientAccounts(self): """Get the primary login for each account managed by the effective user. Returns: tuple Response from the API method. """ method_name = 'getClientAccounts' if self._config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, ()) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (), 'Account', self._loc, request) def GetMccAlerts(self): """Retrieve the MCC alerts. The alerts are associated with any of the accounts beneath the current account. Returns: tuple Response from the API method. """ method_name = 'getMccAlerts' if self._config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, ()) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (), 'Account', self._loc, request) def UpdateAccountInfo(self, acct_info): """Update the account to reflect the changes in the account object. Args: acct_info: dict AccountInfo object with updated values. """ self._sanity_check.ValidateAccountInfoV13(acct_info) method_name = 'updateAccountInfo' if self._config['soap_lib'] == SOAPPY: self.__service.CallMethod(method_name, (acct_info)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) self.__service.CallMethod(method_name, (({'accountInfo': acct_info},)), 'Account', self._loc, request)
class MediaService(ApiService): """Wrapper for MediaService. The MediaService service provides operations for managing media entities. """ def __init__(self, headers, config, op_config, lock, logger): """Inits MediaService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(MediaService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of media objects. Args: selector: dict Filter to run media objects through. Returns: tuple List of media objects meeting all the criteria of the selector. """ method_name = 'getMedia' selector_tag = AdWordsUtils.GetSelectorTag(self._op_config['version']) selector_type = AdWordsUtils.GetSelectorType('MediaSelector', self._op_config['version']) SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, selector_type) if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, selector_tag, self._wsdl_types_map, False, selector_type) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, selector_type, self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({selector_tag: selector},)), 'Media', self._loc, request) def Upload(self, media): """Upload a list of media. Args: media: list Media objects whose byte data should be uploaded. Returns: tuple Uploaded media objects. """ method_name = 'upload' SanityCheck.ValidateTypes(((media, list),)) for item in media: SanityCheck.NewSanityCheck(self._wsdl_types_map, item, 'Media') if self._config['soap_lib'] == SOAPPY: new_media = [] for item in media: new_media.append(self._message_handler.PackVarAsXml( item, 'media', self._wsdl_types_map, False, 'Media')) media = ''.join(new_media) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (media)) elif self._config['soap_lib'] == ZSI: new_media = [] for item in media: new_media.append(self._transformation.MakeZsiCompatible( item, 'Media', self._wsdl_types_map, self._web_services)) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'media': new_media},)), 'Media', self._loc, request)
class TrafficEstimatorService(ApiService): """Wrapper for TrafficEstimatorService. The TrafficEstimatorService service provides operations for estimating campaign, ad group, and keyword traffic. """ def __init__(self, headers, config, op_config, lock, logger): """Inits TrafficEstimatorService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock logger: Logger Instance of Logger """ url = [ op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__ ] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'o') if config['access']: url.insert(len(url) - 1, config['access']) self.__name_space = 'https://adwords.google.com/api/adwords' self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(TrafficEstimatorService, self).__init__(headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def CheckKeywordTraffic(self, requests): """Check a batch of keywords to see whether they will get any traffic. Args: requests: list Requests for keyword traffic checks. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((requests, list), )) for item in requests: self._sanity_check.ValidateKeywordTrafficRequestV13(item) method_name = 'checkKeywordTraffic' if self._config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, (requests)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'requests': requests }, )), 'TrafficEstimator', self._loc, request) def EstimateAdGroupList(self, requests): """Return traffic estimates for the requested set. Set is of new or existing ad groups. Args: requests: list Set of ad groups to estimate. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((requests, list), )) method_name = 'estimateAdGroupList' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck items = [] for item in requests: items.append( self._sanity_check.ValidateAdGroupRequestV13(item)) requests = SoappySanityCheck.UnType(''.join(items)) name_space = '/'.join([ self.__name_space, self._op_config['version'], self._config['access'] ]).strip('/') requests._setAttr('xmlns:impl', name_space) requests._setAttr('xsi3:type', 'AdGroupRequests') return self.__service.CallMethod(method_name, (requests)) elif self._config['soap_lib'] == ZSI: for item in requests: self._sanity_check.ValidateAdGroupRequestV13(item) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'adGroupRequests': requests }, )), 'TrafficEstimator', self._loc, request) def EstimateCampaignList(self, requests): """Return traffic estimates for the requested set of campaigns. Args: requests: list Set of campaigns to estimate. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((requests, list), )) method_name = 'estimateCampaignList' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck items = [] for item in requests: items.append( self._sanity_check.ValidateCampaignRequestV13(item)) requests = SoappySanityCheck.UnType(''.join(items)) name_space = '/'.join([ self.__name_space, self._op_config['version'], self._config['access'] ]).strip('/') requests._setAttr('xmlns:impl', name_space) requests._setAttr('xsi3:type', 'CampaignRequests') return self.__service.CallMethod(method_name, (requests)) elif self._config['soap_lib'] == ZSI: for item in requests: self._sanity_check.ValidateCampaignRequestV13(item) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'campaignRequests': requests }, )), 'TrafficEstimator', self._loc, request) def EstimateKeywordList(self, requests): """Return traffic estimates for the requested set of new keywords. Args: requests: list Set of keywords to estimate. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((requests, list), )) method_name = 'estimateKeywordList' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck new_data = [] for item in requests: new_data.append( SoappySanityCheck.UnType( self._sanity_check.ValidateKeywordRequestV13(item))) return self.__service.CallMethod(method_name, (new_data)) elif self._config['soap_lib'] == ZSI: for item in requests: self._sanity_check.ValidateKeywordRequestV13(item) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'keywordRequests': requests }, )), 'TrafficEstimator', self._loc, request) def Get(self, selector): """Return a list of traffic estimates. Args: selector: dict Filter to run traffic estimate requests through. Returns: tuple List of traffic estimates meeting all the criteria of the selector. """ SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'TrafficEstimatorSelector') method_name = 'getTrafficEstimator' if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'TrafficEstimatorSelector') return self.__service.CallMethod( method_name.split( self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'TrafficEstimatorSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'selector': selector }, )), 'TrafficEstimator', self._loc, request)
class CampaignService(ApiService): """Wrapper for CampaignService. The CampaignService service lets you access, create, list, and modify campaigns and perform campaign-wide operations such as pausing a campaign. """ def __init__(self, headers, config, op_config, lock, logger): """Inits CampaignService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config["server"], "api/adwords", op_config["version"], self.__class__.__name__] if AdWordsSanityCheck.IsJaxbApi(op_config["version"]): url.insert(2, "cm") if config["access"]: url.insert(len(url) - 1, config["access"]) self.__service = AdWordsWebService(headers, config, op_config, "/".join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config["version"]][self.__service._GetServiceName()] super(CampaignService, self).__init__(headers, config, op_config, url, "adspygoogle.adwords", lock, logger) def Get(self, selector): """Return a list of campaigns. List of campaigns specified by the list of selectors from the customer's account. Args: selector: dict Filter to run campaigns through. Returns: tuple List of campaigns meeting all the criteria of the selector. """ method_name = "getCampaign" selector_tag = AdWordsUtils.GetSelectorTag(self._op_config["version"]) selector_type = AdWordsUtils.GetSelectorType("CampaignSelector", self._op_config["version"]) SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, selector_type) if self._config["soap_lib"] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, selector_tag, self._wsdl_types_map, False, selector_type ) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split("Service")[0])[0], (selector) ) elif self._config["soap_lib"] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, selector_type, self._wsdl_types_map, self._web_services ) request = eval("self._web_services.%sRequest()" % method_name) return self.__service.CallMethod(method_name, (({selector_tag: selector},)), "Campaign", self._loc, request) def Mutate(self, ops): """Add, update, or remove campaigns. Args: ops: list Unique operations. Returns: tuple Mutated campaigns. """ method_name = "mutateCampaign" SanityCheck.ValidateTypes(((ops, list),)) for op in ops: SanityCheck.NewSanityCheck(self._wsdl_types_map, op, "CampaignOperation") if self._config["soap_lib"] == SOAPPY: new_ops = [] for op in ops: new_ops.append( self._message_handler.PackVarAsXml( op, "operations", self._wsdl_types_map, False, "CampaignOperation" ) ) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split("Service")[0])[0], ("".join(new_ops)) ) elif self._config["soap_lib"] == ZSI: new_ops = [] for op in ops: new_ops.append( self._transformation.MakeZsiCompatible( op, "CampaignOperation", self._wsdl_types_map, self._web_services ) ) request = eval("self._web_services.%sRequest()" % method_name) return self.__service.CallMethod(method_name, (({"operations": new_ops},)), "Campaign", self._loc, request)
class InfoService(ApiService): """Wrapper for InfoService. The InfoService service allows you to obtain some basic information about your API usage. """ def __init__(self, headers, config, op_config, lock, logger): """Inits InfoService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [ op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__ ] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'info') if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(InfoService, self).__init__(headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def GetUnitSummary(self): """Retrieve the current status of API units. The results are similar to the numbers shown in API Center. Returns: tuple Collection of API units stats. """ selector = {'apiUsageType': 'TOTAL_USAGE_API_UNITS_PER_MONTH'} total_units = self.Get(selector)[0]['cost'] selector = {'apiUsageType': 'FREE_USAGE_API_UNITS_PER_MONTH'} free_units_limit = self.Get(selector)[0]['cost'] selector = { 'dateRange': { 'min': datetime.datetime.now().strftime('%Y%m01'), 'max': datetime.datetime.now().strftime('%Y%m%d') }, 'apiUsageType': 'UNIT_COUNT' } units_count = self.Get(selector)[0]['cost'] if long(units_count) > long(free_units_limit): free_units_used = free_units_limit else: free_units_used = units_count stats = { 'free_units_used': free_units_used, 'total_units_used': units_count, 'free_units_remaining': long(free_units_limit) - long(free_units_used), 'total_units': total_units } return (stats, ) def GetTotalUnitUsed(self): """Retrieve the total number of API units used. Units used from beggining of time to now. Returns: tuple Total number of API units used. """ start_date = '20040101' end_date = datetime.datetime.now().strftime('%Y%m%d') selector = { 'dateRange': { 'min': start_date, 'max': end_date }, 'apiUsageType': 'UNIT_COUNT' } return (self.Get(selector)[0]['cost'], ) def Get(self, selector): """Return the API usage information. Usage information is based on the selection criteria of the selector. Args: selector: dict Filter to run API usage through. Returns: tuple API usage information. """ method_name = 'getInfo' SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'InfoSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'InfoSelector') return self.__service.CallMethod( method_name.split( self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'InfoSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'selector': selector }, )), 'Info', self._loc, request)
class TrafficEstimatorService(ApiService): """Wrapper for TrafficEstimatorService. The TrafficEstimatorService service provides operations for estimating campaign, ad group, and keyword traffic. """ def __init__(self, headers, config, op_config, lock, logger): """Inits TrafficEstimatorService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'o') if config['access']: url.insert(len(url) - 1, config['access']) self.__name_space = 'https://adwords.google.com/api/adwords' self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(TrafficEstimatorService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def CheckKeywordTraffic(self, requests): """Check a batch of keywords to see whether they will get any traffic. Args: requests: list Requests for keyword traffic checks. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((requests, list),)) for item in requests: self._sanity_check.ValidateKeywordTrafficRequestV13(item) method_name = 'checkKeywordTraffic' if self._config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, (requests)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'requests': requests},)), 'TrafficEstimator', self._loc, request) def EstimateAdGroupList(self, requests): """Return traffic estimates for the requested set. Set is of new or existing ad groups. Args: requests: list Set of ad groups to estimate. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((requests, list),)) method_name = 'estimateAdGroupList' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck items = [] for item in requests: items.append(self._sanity_check.ValidateAdGroupRequestV13(item)) requests = SoappySanityCheck.UnType(''.join(items)) name_space = '/'.join([self.__name_space, self._op_config['version'], self._config['access']]).strip('/') requests._setAttr('xmlns:impl', name_space) requests._setAttr('xsi3:type', 'AdGroupRequests') return self.__service.CallMethod(method_name, (requests)) elif self._config['soap_lib'] == ZSI: for item in requests: self._sanity_check.ValidateAdGroupRequestV13(item) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'adGroupRequests': requests},)), 'TrafficEstimator', self._loc, request) def EstimateCampaignList(self, requests): """Return traffic estimates for the requested set of campaigns. Args: requests: list Set of campaigns to estimate. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((requests, list),)) method_name = 'estimateCampaignList' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck items = [] for item in requests: items.append(self._sanity_check.ValidateCampaignRequestV13(item)) requests = SoappySanityCheck.UnType(''.join(items)) name_space = '/'.join([self.__name_space, self._op_config['version'], self._config['access']]).strip('/') requests._setAttr('xmlns:impl', name_space) requests._setAttr('xsi3:type', 'CampaignRequests') return self.__service.CallMethod(method_name, (requests)) elif self._config['soap_lib'] == ZSI: for item in requests: self._sanity_check.ValidateCampaignRequestV13(item) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'campaignRequests': requests},)), 'TrafficEstimator', self._loc, request) def EstimateKeywordList(self, requests): """Return traffic estimates for the requested set of new keywords. Args: requests: list Set of keywords to estimate. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((requests, list),)) method_name = 'estimateKeywordList' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck new_data = [] for item in requests: new_data.append(SoappySanityCheck.UnType( self._sanity_check.ValidateKeywordRequestV13(item))) return self.__service.CallMethod(method_name, (new_data)) elif self._config['soap_lib'] == ZSI: for item in requests: self._sanity_check.ValidateKeywordRequestV13(item) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'keywordRequests': requests},)), 'TrafficEstimator', self._loc, request) def Get(self, selector): """Return a list of traffic estimates. Args: selector: dict Filter to run traffic estimate requests through. Returns: tuple List of traffic estimates meeting all the criteria of the selector. """ SanityCheck.NewSanityCheck( self._wsdl_types_map, selector, 'TrafficEstimatorSelector') method_name = 'getTrafficEstimator' if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'TrafficEstimatorSelector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'TrafficEstimatorSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'selector': selector},)), 'TrafficEstimator', self._loc, request)
class InfoService(ApiService): """Wrapper for InfoService. The InfoService service allows you to obtain some basic information about your API usage. """ def __init__(self, headers, config, op_config, lock, logger): """Inits InfoService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'info') if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(InfoService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def GetUnitSummary(self): """Retrieve the current status of API units. The results are similar to the numbers shown in API Center. Returns: tuple Collection of API units stats. """ selector = { 'apiUsageType': 'TOTAL_USAGE_API_UNITS_PER_MONTH' } total_units = self.Get(selector)[0]['cost'] selector = { 'apiUsageType': 'FREE_USAGE_API_UNITS_PER_MONTH' } free_units_limit = self.Get(selector)[0]['cost'] selector = { 'dateRange': { 'min': datetime.datetime.now().strftime('%Y%m01'), 'max': datetime.datetime.now().strftime('%Y%m%d') }, 'apiUsageType': 'UNIT_COUNT' } units_count = self.Get(selector)[0]['cost'] if long(units_count) > long(free_units_limit): free_units_used = free_units_limit else: free_units_used = units_count stats = { 'free_units_used': free_units_used, 'total_units_used': units_count, 'free_units_remaining': long(free_units_limit) - long(free_units_used), 'total_units': total_units } return (stats,) def GetTotalUnitUsed(self): """Retrieve the total number of API units used. Units used from beggining of time to now. Returns: tuple Total number of API units used. """ start_date = '20040101' end_date = datetime.datetime.now().strftime('%Y%m%d') selector = { 'dateRange': { 'min': start_date, 'max': end_date }, 'apiUsageType': 'UNIT_COUNT' } return (self.Get(selector)[0]['cost'],) def Get(self, selector): """Return the API usage information. Usage information is based on the selection criteria of the selector. Args: selector: dict Filter to run API usage through. Returns: tuple API usage information. """ method_name = 'getInfo' SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'InfoSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'InfoSelector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'InfoSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'selector': selector},)), 'Info', self._loc, request)
class UserListService(ApiService): """Wrapper for UserListService. The UserListService service lets you manage user lists, remarketing user lists, and logical user lists. """ def __init__(self, headers, config, op_config, lock, logger): """Inits UserListService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'cm') if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(UserListService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of user lists. Args: selector: dict Filter to run user lists through. Returns: tuple List of user lists meeting all the criteria of the selector. """ method_name = 'getUserList' selector_tag = AdWordsUtils.GetSelectorTag(self._op_config['version']) selector_type = AdWordsUtils.GetSelectorType('UserListSelector', self._op_config['version']) SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, selector_type) if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, selector_tag, self._wsdl_types_map, False, selector_type) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, selector_type, self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({selector_tag: selector},)), 'UserList', self._loc, request) def Mutate(self, ops): """Add, update, or remove user lists. Args: ops: list Unique operations. Returns: tuple Mutated user lists. """ method_name = 'mutateUserList' SanityCheck.ValidateTypes(((ops, list),)) for op in ops: if 'operand' in op: AdWordsUtils.TransformUserListRuleOperands(op['operand']) SanityCheck.NewSanityCheck(self._wsdl_types_map, op, 'UserListOperation') if self._config['soap_lib'] == SOAPPY: new_ops = [] for op in ops: new_ops.append(self._message_handler.PackVarAsXml( op, 'operations', self._wsdl_types_map, False, 'UserListOperation')) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (''.join(new_ops))) elif self._config['soap_lib'] == ZSI: new_ops = [] for op in ops: new_ops.append(self._transformation.MakeZsiCompatible( op, 'UserListOperation', self._wsdl_types_map, self._web_services)) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'operations': new_ops},)), 'UserList', self._loc, request)
class TargetingIdeaService(ApiService): """Wrapper for TargetingIdeaService. The TargetingIdeaService service provides a way to fetch ideas that match the specified query. """ def __init__(self, headers, config, op_config, lock, logger): """Inits TargetingIdeaService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(TargetingIdeaService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of pages with ideas. List of pages specified by a targeting idea selector. Args: selector: dict Filter to run targeting ideas through. Returns: tuple List of pages with targeting ideas of the selector. """ method_name = 'getTargetingIdea' SanityCheck.NewSanityCheck( self._wsdl_types_map, selector, 'TargetingIdeaSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'TargetingIdeaSelector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'TargetingIdeaSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'selector': selector},)), 'TargetingIdea', self._loc, request) def GetBulkKeywordIdeas(self, selector): """Return a list of pages with ideas. List of pages specified by a targeting idea selector. This method is specialized for returning bulk keyword ideas. Args: selector: dict Filter to run targeting ideas through. Returns: tuple List of pages with targeting ideas of the selector. """ method_name = 'getBulkKeywordIdeas' SanityCheck.NewSanityCheck( self._wsdl_types_map, selector, 'TargetingIdeaSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'TargetingIdeaSelector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'TargetingIdeaSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'selector': selector},)), 'TargetingIdea', self._loc, request)
class ReportService(ApiService): """Wrapper for ReportService. The Report Service allows you to request a report about the performance of your AdWords campaigns. """ def __init__(self, headers, config, op_config, lock, logger): """Inits ReportService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__name_space = 'https://adwords.google.com/api/adwords' self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(ReportService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def DeleteReport(self, report_job_id): """Delete a report job along with the report it produced, if any. Args: report_job_id: str ID of the report job. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)),)) method_name = 'deleteReport' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck report_job_id = SoappySanityCheck.UnType(report_job_id) self.__service.CallMethod(method_name, (report_job_id)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) self.__service.CallMethod(method_name, (({'reportJobId': report_job_id},)), 'Report', self._loc, request) def GetAllJobs(self): """Return an array consisting of all jobs the user has scheduled. Returns: tuple Response from the API method. """ method_name = 'getAllJobs' if self._config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, ()) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (), 'Report', self._loc, request) def GetGzipReportDownloadUrl(self, report_job_id): """Return a URL for a compressed report. URL from which a compressed report with the given job ID can be downloaded (in Gzip format). Args: report_job_id: str ID of the report job. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)),)) method_name = 'getGzipReportDownloadUrl' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck report_job_id = SoappySanityCheck.UnType(report_job_id) return self.__service.CallMethod(method_name, (report_job_id)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'reportJobId': report_job_id},)), 'Report', self._loc, request) def GetReportDownloadUrl(self, report_job_id): """Return a URL for a report. URL from which the report with the given job ID can be downloaded. Args: report_job_id: str ID of the report job. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)),)) method_name = 'getReportDownloadUrl' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck report_job_id = SoappySanityCheck.UnType(report_job_id) return self.__service.CallMethod(method_name, (report_job_id)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'reportJobId': report_job_id},)), 'Report', self._loc, request) def GetReportJobStatus(self, report_job_id): """Return the status of the report job with the given report job ID. Args: report_job_id: str ID of the report job. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)),)) method_name = 'getReportJobStatus' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck report_job_id = SoappySanityCheck.UnType(report_job_id) return self.__service.CallMethod(method_name, (report_job_id)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'reportJobId': report_job_id},)), 'Report', self._loc, request) def ScheduleDefinedReportJob(self, job): """Schedule a defined report job for execution. Args: job: dict Report job object that defines the options for the report. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((job, dict),)) method_name = 'scheduleReportJob' if self._config['soap_lib'] == SOAPPY: name_space = '/'.join([self.__name_space, self._op_config['version'], self._config['access']]).strip('/') job = self._sanity_check.ValidateDefinedReportJobV13(job, name_space) return self.__service.CallMethod(method_name, (job)) elif self._config['soap_lib'] == ZSI: job = self._sanity_check.ValidateDefinedReportJobV13(job, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'job': job},)), 'Report', self._loc, request) def ValidateReportJob(self, job): """This method takes a ReportJob and runs the validation logic against it. Args: job: dict Report job object that defines the options for the report. """ SanityCheck.ValidateTypes(((job, dict),)) method_name = 'validateReportJob' if self._config['soap_lib'] == SOAPPY: name_space = '/'.join([self.__name_space, self._op_config['version'], self._config['access']]).strip('/') job = self._sanity_check.ValidateDefinedReportJobV13(job, name_space) self.__service.CallMethod(method_name, (job)) elif self._config['soap_lib'] == ZSI: job = self._sanity_check.ValidateDefinedReportJobV13(job, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) self.__service.CallMethod(method_name, (({'job': job},)), 'Report', self._loc, request) def DownloadXmlReport(self, report_job_id): """Download and return report data in XML format. Args: report_job_id: str ID of the report job. Returns: str Report data or None if report failed. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)),)) # Wait for report to finish. status = self.GetReportJobStatus(report_job_id)[0] while status != 'Completed' and status != 'Failed': if Utils.BoolTypeConvert(self._config['debug']): print 'Report job status: %s' % status time.sleep(30) status = self.GetReportJobStatus(report_job_id)[0] if status == 'Failed': if Utils.BoolTypeConvert(self._config['debug']): print 'Report process failed' return None if Utils.BoolTypeConvert(self._config['debug']): print 'Report has completed successfully' # Download report. report_url = self.GetGzipReportDownloadUrl(report_job_id)[0] # Convert report into readable XML format. report_xml = urllib.urlopen(report_url).read() return gzip.GzipFile(fileobj=StringIO.StringIO(report_xml)).read() def DownloadCsvReport(self, report_job_id=-1, report_xml=''): """Download and return report data in CSV format. Report consists of two parts: table and totals. Table contains column names and rows of data. The outgoing CSV will contain only columns and data rows. The totals are not included, but can be easily calculated from the CSV data. Args: [optional] report_job_id: str ID of the report job. report_xml: str Report in XML format. Used for testing and debugging. Returns: str Report data if all data is in ASCII. unicode Report data if report contains non-ASCII characters. None If report failed. """ # Get XML report data. if not report_xml and report_job_id > -1: report_xml = self.DownloadXmlReport(report_job_id) if report_xml is None: return None # Prepare XML for DOM construction. report_xml = report_xml.replace('<?xml version="1.0" standalone="yes"?>', '') csv_rows = [] try: # Construct DOM object. dom = minidom.parseString(report_xml) # Get data columns. columns = [] column_dom = dom.getElementsByTagName('column') for column_item in column_dom: if column_item.hasAttributes(): columns.append(column_item.attributes.item(0).value) # Get data rows. rows = [] row_dom = dom.getElementsByTagName('row') for row_item in row_dom: if row_item.hasAttributes(): attrs = row_item.attributes row = {} for index in range(attrs.length): row[attrs.item(index).name] = attrs.item(index).value rows.append(row) # Combine columns and rows into CSV format. csv_rows = [[column for column in columns]] for row in rows: csv_rows.append([row[column] for column in columns]) except: msg = ('Unable to parse report\'s data. Please, file a bug at ' 'http://code.google.com/p/google-api-adwords-python-lib/' 'issues/list.') raise Error(msg) buffer = cStringIO.StringIO() # A workaround for reports that include non-ASCII characters (i.e. ø). try: csv.writer(buffer).writerows(csv_rows) except (UnicodeEncodeError, Exception): unicode_csv_rows = [] for row in csv_rows: unicode_csv_rows.append( ','.join([Utils.CsvEscape(item) for item in row])) return unicode('\n'.join(unicode_csv_rows)) return buffer.getvalue().rstrip()
class BulkMutateJobService(ApiService): """Wrapper for BulkMutateJobService. The BulkMutateJobService service provides operations for submitting jobs to be executed asynchronously and get information about submitted jobs and their parts. """ def __init__(self, headers, config, op_config, lock, logger): """Inits BulkMutateJobService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ # NOTE(api.sgrinberg): Custom handling for BulkMutateJobService, whose # group in URL is 'job/' which is different from its namespace 'cm/'. url = [op_config['server'], 'api/adwords', 'job', op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(BulkMutateJobService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def DownloadBulkJob(self, job_id, wait_secs=30, max_polls=60): """Return results of the bulk mutate job or None if there was a failure. Args: job_id: str Bulk mutate job id. wait_secs: int Time in seconds to wait between each poll. max_polls: int Maximum number of polls to perform. Returns: list Results of the bulk mutate job or None if there was a failure. """ SanityCheck.ValidateTypes(((job_id, (str, unicode)), (wait_secs, int), (max_polls, int))) # Wait for bulk muate job to complete. selector = { 'jobIds': [job_id] } job = self.Get(selector)[0] status = job['status'] num_parts = job['numRequestParts'] num_parts_recieved = job['numRequestPartsReceived'] # Were all parts of the job uploaded? if num_parts != num_parts_recieved: return None num_polls = 1 while (status != 'COMPLETED' and status != 'FAILED' and num_polls < max_polls): if Utils.BoolTypeConvert(self._config['debug']): print 'Bulk mutate job status: %s' % status time.sleep(wait_secs) status = self.Get(selector)[0]['status'] num_polls += 1 if status != 'COMPLETED' and status != 'FAILED' and num_polls >= max_polls: msg = ('The job with id \'%s\' has exceeded max_polls of \'%s\'.' % (job_id, max_polls)) raise AdWordsError(msg) if status == 'FAILED': if Utils.BoolTypeConvert(self._config['debug']): print 'Bulk mutate job failed' return None if Utils.BoolTypeConvert(self._config['debug']): print 'Bulk mutate job completed successfully' # Get results for each part of the job. res = [] for part in xrange(int(num_parts)): selector = { 'jobIds': [job_id], 'resultPartIndex': str(part) } res.append(self.Get(selector)[0]) return res def Get(self, selector): """Return a list of bulk mutate jobs. List of bulk mutate jobs specified by a job selector. Args: selector: dict Filter to run campaign criteria through. Returns: tuple List of bulk mutate jobs meeting all the criteria of the selector. """ method_name = 'getBulkMutateJob' SanityCheck.NewSanityCheck( self._wsdl_types_map, selector, 'BulkMutateJobSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'BulkMutateJobSelector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'BulkMutateJobSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'selector': selector},)), 'BulkMutateJob', self._loc, request) def Mutate(self, op): """Add or update bulk mutate job. Args: op: dict Operation. Returns: tuple Mutated bulk mutate job. """ method_name = 'mutateBulkMutateJob' AdWordsUtils.TransformJobOperationXsi(op) SanityCheck.NewSanityCheck(self._wsdl_types_map, op, 'JobOperation') if self._config['soap_lib'] == SOAPPY: op = self._message_handler.PackVarAsXml( op, 'operation', self._wsdl_types_map, False, 'JobOperation') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (op)) elif self._config['soap_lib'] == ZSI: op = self._transformation.MakeZsiCompatible( op, 'JobOperation', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'operation': op},)), 'BulkMutateJob', self._loc, request)
class CampaignTargetService(ApiService): """Wrapper for CampaignTargetService. The CampaignTargetService service provides operations for accessing, modifying, and creating targets at Campaign level. """ def __init__(self, headers, config, op_config, lock, logger): """Inits CampaignTargetService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [ op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__ ] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(CampaignTargetService, self).__init__(headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of campaign targets. List of campaign targets specified by the selector from the customer's account. Args: selector: dict Filter to run campaign targets through. Returns: tuple List of campaign targets meeting all the criteria of the selector. """ method_name = 'getCampaignTarget' SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'CampaignTargetSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'CampaignTargetSelector') return self.__service.CallMethod( method_name.split( self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'CampaignTargetSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'selector': selector }, )), 'CampaignTarget', self._loc, request) def Mutate(self, ops): """Add, update, or remove campaign targets. Args: ops: list Unique operations. Returns: tuple Mutated campaign targets. """ method_name = 'mutateCampaignTarget' SanityCheck.ValidateTypes(((ops, list), )) for op in ops: SanityCheck.NewSanityCheck(self._wsdl_types_map, op, 'CampaignTargetOperation') if self._config['soap_lib'] == SOAPPY: new_ops = [] for op in ops: new_ops.append( self._message_handler.PackVarAsXml( op, 'operations', self._wsdl_types_map, False, 'CampaignTargetOperation')) return self.__service.CallMethod( method_name.split( self.__class__.__name__.split('Service')[0])[0], (''.join(new_ops))) elif self._config['soap_lib'] == ZSI: new_ops = [] for op in ops: new_ops.append( self._transformation.MakeZsiCompatible( op, 'CampaignTargetOperation', self._wsdl_types_map, self._web_services)) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'operations': new_ops }, )), 'CampaignTarget', self._loc, request)
class ReportService(ApiService): """Wrapper for ReportService. The Report Service allows you to request a report about the performance of your AdWords campaigns. """ def __init__(self, headers, config, op_config, lock, logger): """Inits ReportService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [ op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__ ] if config['access']: url.insert(len(url) - 1, config['access']) self.__name_space = 'https://adwords.google.com/api/adwords' self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(ReportService, self).__init__(headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def DeleteReport(self, report_job_id): """Delete a report job along with the report it produced, if any. Args: report_job_id: str ID of the report job. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)), )) method_name = 'deleteReport' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck report_job_id = SoappySanityCheck.UnType(report_job_id) self.__service.CallMethod(method_name, (report_job_id)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) self.__service.CallMethod(method_name, (({ 'reportJobId': report_job_id }, )), 'Report', self._loc, request) def GetAllJobs(self): """Return an array consisting of all jobs the user has scheduled. Returns: tuple Response from the API method. """ method_name = 'getAllJobs' if self._config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, ()) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (), 'Report', self._loc, request) def GetGzipReportDownloadUrl(self, report_job_id): """Return a URL for a compressed report. URL from which a compressed report with the given job ID can be downloaded (in Gzip format). Args: report_job_id: str ID of the report job. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)), )) method_name = 'getGzipReportDownloadUrl' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck report_job_id = SoappySanityCheck.UnType(report_job_id) return self.__service.CallMethod(method_name, (report_job_id)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'reportJobId': report_job_id }, )), 'Report', self._loc, request) def GetReportDownloadUrl(self, report_job_id): """Return a URL for a report. URL from which the report with the given job ID can be downloaded. Args: report_job_id: str ID of the report job. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)), )) method_name = 'getReportDownloadUrl' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck report_job_id = SoappySanityCheck.UnType(report_job_id) return self.__service.CallMethod(method_name, (report_job_id)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'reportJobId': report_job_id }, )), 'Report', self._loc, request) def GetReportJobStatus(self, report_job_id): """Return the status of the report job with the given report job ID. Args: report_job_id: str ID of the report job. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)), )) method_name = 'getReportJobStatus' if self._config['soap_lib'] == SOAPPY: from adspygoogle.common.soappy import SanityCheck as SoappySanityCheck report_job_id = SoappySanityCheck.UnType(report_job_id) return self.__service.CallMethod(method_name, (report_job_id)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'reportJobId': report_job_id }, )), 'Report', self._loc, request) def ScheduleDefinedReportJob(self, job): """Schedule a defined report job for execution. Args: job: dict Report job object that defines the options for the report. Returns: tuple Response from the API method. """ SanityCheck.ValidateTypes(((job, dict), )) method_name = 'scheduleReportJob' if self._config['soap_lib'] == SOAPPY: name_space = '/'.join([ self.__name_space, self._op_config['version'], self._config['access'] ]).strip('/') job = self._sanity_check.ValidateDefinedReportJobV13( job, name_space) return self.__service.CallMethod(method_name, (job)) elif self._config['soap_lib'] == ZSI: job = self._sanity_check.ValidateDefinedReportJobV13( job, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'job': job }, )), 'Report', self._loc, request) def ValidateReportJob(self, job): """This method takes a ReportJob and runs the validation logic against it. Args: job: dict Report job object that defines the options for the report. """ SanityCheck.ValidateTypes(((job, dict), )) method_name = 'validateReportJob' if self._config['soap_lib'] == SOAPPY: name_space = '/'.join([ self.__name_space, self._op_config['version'], self._config['access'] ]).strip('/') job = self._sanity_check.ValidateDefinedReportJobV13( job, name_space) self.__service.CallMethod(method_name, (job)) elif self._config['soap_lib'] == ZSI: job = self._sanity_check.ValidateDefinedReportJobV13( job, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) self.__service.CallMethod(method_name, (({ 'job': job }, )), 'Report', self._loc, request) def DownloadXmlReport(self, report_job_id): """Download and return report data in XML format. Args: report_job_id: str ID of the report job. Returns: str Report data or None if report failed. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)), )) # Wait for report to finish. status = self.GetReportJobStatus(report_job_id)[0] while status != 'Completed' and status != 'Failed': if Utils.BoolTypeConvert(self._config['debug']): print 'Report job status: %s' % status time.sleep(30) status = self.GetReportJobStatus(report_job_id)[0] if status == 'Failed': if Utils.BoolTypeConvert(self._config['debug']): print 'Report process failed' return None if Utils.BoolTypeConvert(self._config['debug']): print 'Report has completed successfully' # Download report. report_url = self.GetGzipReportDownloadUrl(report_job_id)[0] # Convert report into readable XML format. report_xml = urllib.urlopen(report_url).read() return gzip.GzipFile(fileobj=StringIO.StringIO(report_xml)).read() def DownloadCsvReport(self, report_job_id=-1, report_xml=''): """Download and return report data in CSV format. Report consists of two parts: table and totals. Table contains column names and rows of data. The outgoing CSV will contain only columns and data rows. The totals are not included, but can be easily calculated from the CSV data. Args: [optional] report_job_id: str ID of the report job. report_xml: str Report in XML format. Used for testing and debugging. Returns: str Report data if all data is in ASCII. unicode Report data if report contains non-ASCII characters. None If report failed. """ # Get XML report data. if not report_xml and report_job_id > -1: report_xml = self.DownloadXmlReport(report_job_id) if report_xml is None: return None # Prepare XML for DOM construction. report_xml = report_xml.replace( '<?xml version="1.0" standalone="yes"?>', '') csv_rows = [] try: # Construct DOM object. dom = minidom.parseString(report_xml) # Get data columns. columns = [] column_dom = dom.getElementsByTagName('column') for column_item in column_dom: if column_item.hasAttributes(): columns.append(column_item.attributes.item(0).value) # Get data rows. rows = [] row_dom = dom.getElementsByTagName('row') for row_item in row_dom: if row_item.hasAttributes(): attrs = row_item.attributes row = {} for index in range(attrs.length): row[attrs.item(index).name] = attrs.item(index).value rows.append(row) # Combine columns and rows into CSV format. csv_rows = [[column for column in columns]] for row in rows: csv_rows.append([row[column] for column in columns]) except: msg = ('Unable to parse report\'s data. Please, file a bug at ' 'http://code.google.com/p/google-api-adwords-python-lib/' 'issues/list.') raise Error(msg) buffer = cStringIO.StringIO() # A workaround for reports that include non-ASCII characters (i.e. ø). try: csv.writer(buffer).writerows(csv_rows) except (UnicodeEncodeError, Exception): unicode_csv_rows = [] for row in csv_rows: unicode_csv_rows.append(','.join( [Utils.CsvEscape(item) for item in row])) return unicode('\n'.join(unicode_csv_rows)) return buffer.getvalue().rstrip()
class ReportDefinitionService(ApiService): """Wrapper for ReportDefinitionService. The ReportDefinitionService service provides operations for accessing, modifying, and creating report definitions. """ def __init__(self, headers, config, op_config, lock, logger): """Inits ReportDefinitionService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(ReportDefinitionService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of report definitions. Args: selector: dict Filter to run report definitions through. Returns: tuple List of report definitions meeting all the criteria of the selector. """ method_name = 'getReportDefinition' SanityCheck.NewSanityCheck( self._wsdl_types_map, selector, 'ReportDefinitionSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'ReportDefinitionSelector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'ReportDefinitionSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'selector': selector},)), 'ReportDefinition', self._loc, request) def GetReportFields(self, report_type): """Return a list of supported report fields. Args: report_type: str Type of the report. Returns: tuple List of report fields. """ SanityCheck.ValidateTypes(((report_type, str),)) method_name = 'getReportFields' if self._config['soap_lib'] == SOAPPY: report_type = self._message_handler.PackVarAsXml(report_type, 'reportType') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (report_type)) elif self._config['soap_lib'] == ZSI: request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'reportType': report_type},)), 'ReportDefinition', self._loc, request) def Mutate(self, ops): """Create, update, or remove a report. Args: ops: list Unique operations. Returns: tuple Mutated reports. """ method_name = 'mutateReportDefinition' SanityCheck.ValidateTypes(((ops, list),)) for op in ops: SanityCheck.NewSanityCheck( self._wsdl_types_map, op, 'ReportDefinitionOperation') if self._config['soap_lib'] == SOAPPY: new_ops = [] for op in ops: new_ops.append(self._message_handler.PackVarAsXml( op, 'operations', self._wsdl_types_map, False, 'ReportDefinitionOperation')) ops = ''.join(new_ops) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (ops)) elif self._config['soap_lib'] == ZSI: for op in ops: op = self._transformation.MakeZsiCompatible( op, 'ReportDefinitionOperation', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'operations': ops},)), 'ReportDefinition', self._loc, request) def DownloadReport(self, report_definition_id, return_micros=False): """Download report and return raw data. Args: report_definition_id: str Id of the report definition to download. return_micros: bool Whether to return currency in micros. Returns: str Report data. """ self.__ReloadAuthToken() selector = self.__GenerateUrl(report_definition_id) headers = self.__GenerateHeaders(return_micros) response = self.__MakeRequest(selector, headers) return response['body'] def __GenerateUrl(self, report_definition_id, query_token=None): """Generates the URL to get a report from. Args: report_definition_id: int ID of the report to download. query_token: str query token (if one is needed/exists). Returns: str url to request """ url = REPORT_DOWNLOAD_URL % report_definition_id if query_token: url += ('&qt=%s' % query_token) return url def __GenerateHeaders(self, return_micros): """Generates the headers to use for the report download. Args: return_micros: bool whether or not to use micros for money. Returns: dict Dictionary containing all the headers for the request """ headers = {} if ('clientEmail' in self.__service._headers and self.__service._headers['clientEmail']): headers['clientEmail'] = self.__service._headers['clientEmail'] elif 'clientCustomerId' in self.__service._headers: headers['clientCustomerId'] = self.__service._headers['clientCustomerId'] headers['Authorization'] = ('GoogleLogin auth=%s' % self.__service._headers['authToken'].strip()) headers['returnMoneyInMicros'] = str(return_micros).lower() return headers def __MakeRequest(self, url, headers=None, file_path=None): """Performs an HTTPS request and slightly processes the response. If file_path is provided, saves the body to file instead of including it in the return value. Args: url: str Resource for the request line. headers: dict Headers to send along with the request. file_path: str File to save to (optional). Returns: dict process response with the following structure {'body': 'body of response', 'status': 'http status code', 'headers': 'headers that came back with the response'} """ headers = headers or {} # Download report. conn = httplib.HTTPSConnection( Utils.GetNetLocFromUrl(self._op_config['server'])) conn.connect() conn.putrequest('GET', url) for key in headers: conn.putheader(key, headers[key]) conn.endheaders() response = conn.getresponse() response_headers = {} for key, value in response.getheaders(): response_headers[key] = value body = None if file_path: self.__DumpToFile(response, file_path) else: body = response.read() return { 'body': body, 'status': response.status, 'headers': response_headers, 'reason': response.reason } def __ReloadAuthToken(self): """Ensures we have a valid auth_token in our headers.""" # Load/set authentication token. If authentication token has expired, # regenerate it. now = time.time() if (('authToken' not in self.__service._headers and 'auth_token_epoch' not in self._config) or int(now - self._config['auth_token_epoch']) >= AUTH_TOKEN_EXPIRE): if ('email' not in self.__service._headers or not self._headers['email'] or 'password' not in self.__service._headers or not self.__service._headers['password']): msg = ('Required authentication headers, \'email\' and \'password\', ' 'are missing. Unable to regenerate authentication token.') raise ValidationError(msg) self._headers['authToken'] = Utils.GetAuthToken( self.__service._headers['email'], self.__service._headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, self._config['proxy']) self._config['auth_token_epoch'] = time.time() def DownloadMCCReport(self, report_definition_id, file_path=None, return_micros=False, tries=50, sleep=30): """Blocking call to download a MCC report. This method will try to successfully download the report to the specified file_path, throwing an AdWordsApiError. If file_path is None or not provided, will return as a string the downloaded report. Args: report_definition_id: str Id of the report definition to download. file_path: str File path to download to. Optional, returns string if None. return_micros: bool Whether to return currency in micros. tries: int Optional number of times to retry. sleep: int Optional number of seconds to sleep between retries. Returns: str Report data or a tuple of filename and bytes written if file_path is not None. Raises: AdWordsApiError: When we encounter a server error """ response = self.GetMCCReportStatus(report_definition_id, return_micros, 'new') while response['httpStatus'] != 301 and tries > 0: if response['httpStatus'] != 200: msg = [] if 'state' in response: msg.append(response['state']) if 'failureReason' in response: msg.append(response['failureReason']) raise AdWordsApiError({ 'faultcode': '500', 'faultstring': ' '.join(msg)}) # Otherwise, it should be status == 200 which means try again later. if Utils.BoolTypeConvert(self._config['debug']): print 'Sleeping with %i tries left' % tries tries -= 1 time.sleep(sleep) response = self.GetMCCReportStatus(report_definition_id, return_micros, response['queryToken']) if tries <= 0: raise AdWordsApiError({'faultstring': 'No more retries left.'}) # On success, get the download URL from the Location header url = None if 'Location' in response['headers']: url = response['headers']['Location'] else: url = response['headers']['location'] headers = self.__GenerateHeaders(return_micros) return self.__DownloadFile(url, headers, file_path) def GetMCCReportStatus(self, report_definition_id, return_micros=False, query_token='new'): """Tries to download report and return raw data. Does not poll. Args: report_definition_id: str Id of the report definition to download. return_micros: bool Whether to return currency in micros. query_token: str Query token to use, defaults to 'new'. Returns: str Report data. """ self.__ReloadAuthToken() selector = self.__GenerateUrl(report_definition_id, query_token) headers = self.__GenerateHeaders(return_micros) parsed_response = self.__ParseMCCResponse(self.__MakeRequest( selector, headers)) return parsed_response def __ParseMCCResponse(self, http_response): """Parses the raw MCC response xml into a dict. Args: http_response: dict The slightly processed response from the server. Returns: dict Dictionary interpretation of the response. Format is ret[tagName] = 'value' for all tags in the xml response, except for the failed account ids which reside in 'failedIds'. We also store httpStatus, headers (as a dict), and the raw xml itself in the tags 'httpStatus', 'headers' and 'raw' """ self.__CheckOldError(http_response['body']) doc = minidom.parseString(http_response['body']) ret = {} for tag in ['queryToken', 'state', 'total', 'success', 'fail', 'failureReason']: ret[tag] = self.__GetText(doc, tag) ret['failedIds'] = self.__GetText(doc, 'id') ret['httpStatus'] = http_response['status'] ret['headers'] = http_response['headers'] ret['raw'] = http_response['body'] return ret def __GetText(self, doc, tag): """Extracts text from elements matching tag name. Args: doc: DOM DOM model of the xml. tag: str Name of the tag to get text for. Returns: str String value of a single result node. list List of string nodes if more than one tag matches. """ nodelist = doc.getElementsByTagName(tag) ret = [] for node in nodelist: text_nodes = [] for text_node in node.childNodes: if text_node.nodeType == text_node.TEXT_NODE: text_nodes.append(text_node.data) if text_nodes: ret.append(''.join(text_nodes)) # return empty string if we have no text if not ret: return '' # if only one, return just the single element if len(ret) == 1: return ret[0] return ret def __CheckOldError(self, response_text): """Checks for the old-style error messages. Args: response_text: str Raw text the server returns. Raises: AdWordsApiError: When we see an old-style error. """ error_message_regex = OLD_ERROR_REGEX matches = re.search(error_message_regex, response_text) if matches: message = response_text if matches.group(3): message = matches.group(3) raise AdWordsApiError({'faultstring': message}) def __DownloadFile(self, url, headers, file_path=None): """Downloads the specified url. Args: url: str Full URL to download. headers: dict Headers to use with the request. file_path: str Optional path to download to. If None, text is returned. Returns: str File contents if file_path is None or else a tuple with filename and bytes written. """ req = urllib2.Request(url, None, headers) r = urllib2.urlopen(req) # if we have a file, write to it, otherwise return the text if file_path: return self.__DumpToFile(r, file_path) else: return r.read() def __DumpToFile(self, response, file_path): """Reads from response.read() and writes to file_path. Args: response: file Some object that supports read(). file_path: str File name to write to. Returns: tuple Filename and number of bytes written. """ byteswritten = 0 f = open(file_path, 'w+') while True: buf = response.read(BUF_SIZE) if buf: f.write(buf) byteswritten += len(buf) else: break return (file_path, byteswritten)
class DataService(ApiService): """Wrapper for DataService. The DataService service lets you retrieve Ads Campaign Management data matching a selector. """ def __init__(self, headers, config, op_config, lock, logger): """Inits DataService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'cm') if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(DataService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def GetAdGroupBidLandscape(self, selector): """Return a list of bid landscapes for the ad groups in the selector. Args: selector: dict Filter to run bid landscapes through. Returns: tuple List of bid landscapes meeting all the criteria of the selector. """ method_name = 'getAdGroupBidLandscape' SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'Selector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'serviceSelector', self._wsdl_types_map, False, 'Selector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'Selector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'serviceSelector': selector},)), 'Data', self._loc, request) def GetCriterionBidLandscape(self, selector): """Return a list of bid landscapes for the criteria in the selector. Args: selector: dict Filter to run bid landscapes through. Returns: tuple List of bid landscapes meeting all the criteria of the selector. """ method_name = 'getCriterionBidLandscape' SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'Selector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'serviceSelector', self._wsdl_types_map, False, 'Selector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'Selector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'serviceSelector': selector},)), 'Data', self._loc, request)
class AdParamService(ApiService): """Wrapper for AdParamService. The AdParamService service provides operations for accessing, creating, modifying, and removing ad parameters. """ def __init__(self, headers, config, op_config, lock, logger): """Inits AdParamService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(AdParamService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of ad params. List of ad params specified by the selector from the customer's account. Args: selector: dict Filter to run ad params through. Returns: tuple List of ad params meeting all the criteria of the selector. """ method_name = 'getAdParam' SanityCheck.NewSanityCheck( self._wsdl_types_map, selector, 'AdParamSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'AdParamSelector') return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'AdParamSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'selector': selector},)), 'AdParam', self._loc, request) def Mutate(self, ops): """Create, update, or remove ad params. Args: ops: list Unique operations. Returns: tuple Mutated ad params. """ method_name = 'mutateAdParam' SanityCheck.ValidateTypes(((ops, list),)) for op in ops: SanityCheck.NewSanityCheck(self._wsdl_types_map, op, 'AdParamOperation') if self._config['soap_lib'] == SOAPPY: new_ops = [] for op in ops: new_ops.append(self._message_handler.PackVarAsXml( op, 'operations', self._wsdl_types_map, False, 'AdParamOperation')) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (''.join(new_ops))) elif self._config['soap_lib'] == ZSI: new_ops = [] for op in ops: new_ops.append(self._transformation.MakeZsiCompatible( op, 'AdParamOperation', self._wsdl_types_map, self._web_services)) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'operations': new_ops},)), 'AdParam', self._loc, request)
class ConversionTrackerService(ApiService): """Wrapper for ConversionTrackerService. The ConversionTrackerService service lets you manage conversion trackers. """ def __init__(self, headers, config, op_config, lock, logger): """Inits ConversionTrackerService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if AdWordsSanityCheck.IsJaxbApi(op_config['version']): url.insert(2, 'cm') if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(ConversionTrackerService, self).__init__( headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of conversion trackers. Args: selector: dict Filter to run user lists through. Returns: tuple List of user lists meeting all the criteria of the selector. """ method_name = 'getConversionTracker' selector_tag = AdWordsUtils.GetSelectorTag(self._op_config['version']) selector_type = AdWordsUtils.GetSelectorType('ConversionTrackerSelector', self._op_config['version']) SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, selector_type) if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, selector_tag, self._wsdl_types_map, False, selector_type) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, selector_type, self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({selector_tag: selector},)), 'ConversionTracker', self._loc, request) def Mutate(self, ops): """Add, update, or remove conversion trackers. Args: ops: list Unique operations. Returns: tuple Mutated user lists. """ method_name = 'mutateConversionTracker' SanityCheck.ValidateTypes(((ops, list),)) for op in ops: SanityCheck.NewSanityCheck( self._wsdl_types_map, op, 'ConversionTrackerOperation') if self._config['soap_lib'] == SOAPPY: SanityCheck.ValidateTypes(((ops, list),)) new_ops = [] for op in ops: new_ops.append(self._message_handler.PackVarAsXml( op, 'operations', self._wsdl_types_map, False, 'ConversionTrackerOperation')) return self.__service.CallMethod( method_name.split(self.__class__.__name__.split('Service')[0])[0], (''.join(new_ops))) elif self._config['soap_lib'] == ZSI: new_ops = [] for op in ops: new_ops.append(self._transformation.MakeZsiCompatible( op, 'ConversionTrackerOperation', self._wsdl_types_map, self._web_services)) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'operations': new_ops},)), 'ConversionTracker', self._loc, request)
class MediaService(ApiService): """Wrapper for MediaService. The MediaService service provides operations for managing media entities. """ def __init__(self, headers, config, op_config, lock, logger): """Inits MediaService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [ op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__ ] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(MediaService, self).__init__(headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of media objects. Args: selector: dict Filter to run media objects through. Returns: tuple List of media objects meeting all the criteria of the selector. """ method_name = 'getMedia' selector_tag = AdWordsUtils.GetSelectorTag(self._op_config['version']) selector_type = AdWordsUtils.GetSelectorType( 'MediaSelector', self._op_config['version']) SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, selector_type) if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, selector_tag, self._wsdl_types_map, False, selector_type) return self.__service.CallMethod( method_name.split( self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, selector_type, self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ selector_tag: selector }, )), 'Media', self._loc, request) def Upload(self, media): """Upload a list of media. Args: media: list Media objects whose byte data should be uploaded. Returns: tuple Uploaded media objects. """ method_name = 'upload' SanityCheck.ValidateTypes(((media, list), )) for item in media: SanityCheck.NewSanityCheck(self._wsdl_types_map, item, 'Media') if self._config['soap_lib'] == SOAPPY: new_media = [] for item in media: new_media.append( self._message_handler.PackVarAsXml(item, 'media', self._wsdl_types_map, False, 'Media')) media = ''.join(new_media) return self.__service.CallMethod( method_name.split( self.__class__.__name__.split('Service')[0])[0], (media)) elif self._config['soap_lib'] == ZSI: new_media = [] for item in media: new_media.append( self._transformation.MakeZsiCompatible( item, 'Media', self._wsdl_types_map, self._web_services)) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'media': new_media }, )), 'Media', self._loc, request)
class TargetingIdeaService(ApiService): """Wrapper for TargetingIdeaService. The TargetingIdeaService service provides a way to fetch ideas that match the specified query. """ def __init__(self, headers, config, op_config, lock, logger): """Inits TargetingIdeaService. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. op_config: dict Dictionary object with additional configuration values for this operation. lock: thread.lock Thread lock. logger: Logger Instance of Logger """ url = [ op_config['server'], 'api/adwords', op_config['group'], op_config['version'], self.__class__.__name__ ] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = AdWordsWebService(headers, config, op_config, '/'.join(url), lock, logger) self._wsdl_types_map = WSDL_MAP[op_config['version']][ self.__service._GetServiceName()] super(TargetingIdeaService, self).__init__(headers, config, op_config, url, 'adspygoogle.adwords', lock, logger) def Get(self, selector): """Return a list of pages with ideas. List of pages specified by a targeting idea selector. Args: selector: dict Filter to run targeting ideas through. Returns: tuple List of pages with targeting ideas of the selector. """ method_name = 'getTargetingIdea' SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'TargetingIdeaSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'TargetingIdeaSelector') return self.__service.CallMethod( method_name.split( self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'TargetingIdeaSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'selector': selector }, )), 'TargetingIdea', self._loc, request) def GetBulkKeywordIdeas(self, selector): """Return a list of pages with ideas. List of pages specified by a targeting idea selector. This method is specialized for returning bulk keyword ideas. Args: selector: dict Filter to run targeting ideas through. Returns: tuple List of pages with targeting ideas of the selector. """ method_name = 'getBulkKeywordIdeas' SanityCheck.NewSanityCheck(self._wsdl_types_map, selector, 'TargetingIdeaSelector') if self._config['soap_lib'] == SOAPPY: selector = self._message_handler.PackVarAsXml( selector, 'selector', self._wsdl_types_map, False, 'TargetingIdeaSelector') return self.__service.CallMethod( method_name.split( self.__class__.__name__.split('Service')[0])[0], (selector)) elif self._config['soap_lib'] == ZSI: selector = self._transformation.MakeZsiCompatible( selector, 'TargetingIdeaSelector', self._wsdl_types_map, self._web_services) request = eval('self._web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({ 'selector': selector }, )), 'TargetingIdea', self._loc, request)