def DownloadReportToFile(report_job_id, export_format, service, outfile): """Download report data and write to a file. Args: report_job_id: str ID of the report job. export_format: str Export format for the report file. service: GenericDfpService A service pointing to the ReportService. outfile: file the file object to write to. If no file handle is passed in, a temporary file will be created. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)),)) # Wait for report to complete. status = service.GetReportJob(report_job_id)[0]['reportJobStatus'] while status != 'COMPLETED' and status != 'FAILED': if Utils.BoolTypeConvert(service._config['debug']): print 'Report job status: %s' % status time.sleep(30) status = service.GetReportJob(report_job_id)[0]['reportJobStatus'] if status == 'FAILED': if Utils.BoolTypeConvert(service._config['debug']): print 'Report process failed' return '' else: if Utils.BoolTypeConvert(service._config['debug']): print 'Report has completed successfully' # Get report download URL. report_url = service.GetReportDownloadURL(report_job_id, export_format)[0] response = urllib2.urlopen(report_url) while True: chunk = response.read(_CHUNK_SIZE) if not chunk: break outfile.write(chunk)
def __LogRequest(self, xml_log_data): """Logs the Report Download request. Args: xml_log_data: str Data to log for this request. """ log_handlers = self.__GetLogHandlers() for handler in log_handlers: handler['target'] = Logger.NONE if handler['tag'] == 'xml_log': handler['data'] += xml_log_data for handler in log_handlers: if (handler['tag'] and Utils.BoolTypeConvert(self._config[handler['tag']])): handler['target'] = Logger.FILE # If debugging is On, raise handler's target two levels, # NONE -> CONSOLE # FILE -> FILE_AND_CONSOLE. if Utils.BoolTypeConvert(self._config['debug']): handler['target'] += 2 if (handler['target'] != Logger.NONE and handler['data'] and handler['data'] != 'None' and handler['data'] != 'DEBUG: '): self._logger.Log(handler['name'], handler['data'], log_level=Logger.DEBUG, log_handler=handler['target'])
def DownloadReport(self, report_job_id, export_format): """Download and return report data. Args: report_job_id: str ID of the report job. export_format: str Export format for the report file. Returns: str Report data or empty string if report failed. """ SanityCheck.ValidateTypes(((report_job_id, (str, unicode)),)) # Wait for report to complete. status = self.GetReportJob(report_job_id)[0]['reportJobStatus'] while status != 'COMPLETED' and status != 'FAILED': if Utils.BoolTypeConvert(self._config['debug']): print 'Report job status: %s' % status time.sleep(30) status = self.GetReportJob(report_job_id)[0]['reportJobStatus'] if status == 'FAILED': if Utils.BoolTypeConvert(self._config['debug']): print 'Report process failed' return '' else: if Utils.BoolTypeConvert(self._config['debug']): print 'Report has completed successfully' # Get report download URL. report_url = self.GetReportDownloadURL(report_job_id, export_format)[0] # Download report. data = urllib.urlopen(report_url).read() data = gzip.GzipFile(fileobj=StringIO.StringIO(data)).read() return data
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 GetServiceConnection(headers, config, url, http_proxy, is_jaxb_api): """Get SOAP service connection. Args: headers: dict Dictionary object with populated authentication credentials. config: dict Dictionary object with populated configuration values. url: str URL of the web service to call. http_proxy: str HTTP proxy to use. is_jaxb_api: bool Whether API uses JAXB. Returns: instance SOAPpy.SOAPProxy with set headers. """ # Catch empty SOAP header elements and exclude them from request. full_headers = {} for key in headers: if headers[key]: full_headers[key] = headers[key] if is_jaxb_api or Utils.BoolTypeConvert(config['wsse']): headers = SOAPpy.Types.headerType( {config['ns_target'][1]: full_headers}) headers._setAttr('xmlns', config['ns_target'][0]) service = SOAPpy.SOAPProxy(url, http_proxy=http_proxy, header=headers, transport=HTTPTransportHandler) service.transport.data_injects = config['data_injects'] elif Utils.BoolTypeConvert(config['force_data_inject']): service = SOAPpy.SOAPProxy(url, http_proxy=http_proxy, header=headers, transport=HTTPTransportHandler) service.transport.data_injects = config['data_injects'] else: headers = SOAPpy.Types.headerType(full_headers) service = SOAPpy.SOAPProxy(url, http_proxy=http_proxy, header=headers) service.config.dumpHeadersIn = 1 service.config.dumpHeadersOut = 1 service.config.dumpSOAPIn = 1 service.config.dumpSOAPOut = 1 # Turn off type information, since SOAPpy usually gets the types wrong. service.config.typed = 0 # Turn on noroot, to skip including "SOAP-ENC:root" as part of the request. service.noroot = 1 # Explicitly set the style of the namespace, otherwise will default to 1999. service.config.namespaceStyle = '2001' # Set up OAuth, if applicable. if 'oauth_enabled' in config and config['oauth_enabled']: service.transport.oauth_enabled = True service.transport.oauth_handler = config['oauth_handler'] service.transport.oauth_credentials = config['oauth_credentials'] return service
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 GetServiceConnection(headers, config, op_config, url, service_name, loc): """Get SOAP service connection. 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. url: str URL of the web service to call. service_name: str API service name. loc: Service locator. Returns: instance of SoapBindingSOAP interface with set headers. """ kw = {'tracefile': sys.stdout} use_conn_handler = False if Utils.BoolTypeConvert(config['raw_debug']): HttpConnectionHandler.debug = True HttpsConnectionHandler.debug = True use_conn_handler = True if Utils.BoolTypeConvert(config['compress']): HttpConnectionHandler.compress = True HttpsConnectionHandler.compress = True use_conn_handler = True if use_conn_handler and op_config['http_proxy']: HttpConnectionHandler.http_proxy = op_config['http_proxy'] kw['transport'] = HttpConnectionHandler elif use_conn_handler: kw['transport'] = HttpsConnectionHandler if 'ns_target' in config: service_name += 'Service' port_type = eval('loc.get%sInterface(url=url, **kw)' % service_name) group = None if 'default_group' in op_config: group = op_config['default_group'] port_type.binding.sig_handler = SigHandler(headers, config, group) # Set custom HTTP headers. postfix = '' if Utils.BoolTypeConvert(config['compress']): postfix = ', gzip' user_agent = 'ZSI %s%s' % ('.'.join(map(str, ZSI_VERSION)), postfix) port_type.binding.AddHeader('User-Agent', user_agent) # Handle OAuth (if enabled) if 'oauth_enabled' in config and config['oauth_enabled']: signedrequestparams = config[ 'oauth_handler'].GetSignedRequestParameters( config['oauth_credentials'], url) port_type.binding.AddHeader( 'Authorization', 'OAuth ' + config['oauth_handler'].FormatParametersForHeader( signedrequestparams)) return port_type
def CallRawMethod(self, soap_message): """Make an API call by posting raw SOAP XML message. Args: soap_message: str SOAP XML message. Returns: tuple Response from the API method. """ # Acquire thread lock. self._lock.acquire() try: buf = AdWordsSoapBuffer(xml_parser=self._config['xml_parser'], pretty_xml=Utils.BoolTypeConvert( self._config['pretty_xml'])) super(AdWordsWebService, self).CallRawMethod( buf, Utils.GetNetLocFromUrl(self._op_config['server']), soap_message) self.__ManageSoap(buf, self._start_time, self._stop_time, {'data': buf.GetBufferAsStr()}) finally: # Release thread lock. if self._lock.locked(): self._lock.release() return (self._response, )
def __SetUseStrict(self, new_state): """Temporarily change strictness mode for a given AdWordsClient instance. Args: new_state: bool New state of the strictness mode. """ self._config['strict'] = Utils.BoolTypeConvert(new_state, str)
def __SetRawDebug(self, new_state): """Temporarily change raw debug mode for a given AdWordsClient instance. Args: new_state: bool New state of the raw debug mode. """ self._config['raw_debug'] = Utils.BoolTypeConvert(new_state, str)
def GetService(self, service_name, server='https://www.google.com', version=None, http_proxy=None, op_config=None): """Generic method to create a service. Args: service_name: str Name of the service to create. [optional] server: str API server to access for API calls. The default value is 'https://www.google.com'. version: str API version to use. http_proxy: str HTTP proxy to use. op_config: dict service configuration. Returns: GenericDfpService New object representing the SOAP service. """ if version is None: version = DEFAULT_API_VERSION if Utils.BoolTypeConvert(self._config['strict']): DfpSanityCheck.ValidateServer(server, version) # Load additional configuration data. if op_config is None: op_config = { 'server': server, 'version': version, 'http_proxy': http_proxy } return GenericDfpService(self._headers, self._config, op_config, self.__lock, self.__logger, service_name)
def SetDebug(self, new_state): """Temporarily change debug mode for a given Client instance. Args: new_state: bool New state of the debug mode. """ self._config['debug'] = Utils.BoolTypeConvert(new_state, str)
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 'clientCustomerId' in self._headers: headers['clientCustomerId'] = self._headers['clientCustomerId'] # Handle OAuth (if enabled) and ClientLogin if self._headers.get('oauth2credentials'): self._headers['oauth2credentials'].apply(headers) else: headers['Authorization'] = ('GoogleLogin %s' % urllib.urlencode({'auth': self._headers['authToken'].strip()})) headers['returnMoneyInMicros'] = str(return_micros).lower() headers['developerToken'] = self._headers['developerToken'] headers['User-Agent'] = self._headers['userAgent'] if Utils.BoolTypeConvert(self._config['compress']): headers['Accept-Encoding'] = 'gzip' headers['User-Agent'] += ',gzip' headers['Content-Encoding'] = 'gzip' return headers
def __MakeRequest(self, url, headers=None, fileobj=None, payload=None): """Performs an HTTPS request and slightly processes the response. If fileobj 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. fileobj: file File to save to (optional). payload: str Xml to POST (optional). Returns: str Report data as a string if fileobj=None, otherwise None """ headers = headers or {} request_url = self._op_config['server'] + url orig_payload = payload if Utils.BoolTypeConvert(self._config['compress']): buffer = StringIO.StringIO() gzip_file = gzip.GzipFile(mode='wb', fileobj=buffer) gzip_file.write(payload) gzip_file.close() payload = buffer.getvalue() headers['Content-Length'] = str(len(payload)) start_time = time.strftime('%Y-%m-%d %H:%M:%S') request = urllib2.Request(request_url, payload, headers) try: try: response = urllib2.urlopen(request) response_code = response.code response_headers = response.info().headers if response.info().get('Content-Encoding') == 'gzip': response = gzip.GzipFile(fileobj=StringIO.StringIO( response.read()), mode='rb') if fileobj: self.__DumpToFile(response, fileobj) return None else: return response.read() except urllib2.HTTPError, e: response = e response_code = response.code response_headers = response.info().headers if response.info().get('Content-Encoding') == 'gzip': response = gzip.GzipFile(fileobj=StringIO.StringIO( response.read()), mode='rb') error = response.read() self.__CheckForXmlError(response_code, error) raise AdWordsError('%s %s' % (str(e), error)) except urllib2.URLError, e: response = e response_code = '---' response_headers = [] raise AdWordsError(str(e))
def GetUserService(self, server='https://sandbox.google.com', version=None, http_proxy=None): """Call API method in UserService. Args: [optional] server: str API server to access for this API call. Possible values are: 'https://www.google.com' for live site and 'https://sandbox.google.com' for sandbox. The default behavior is to access sandbox site. version: str API version to use. http_proxy: str HTTP proxy to use. Returns: UserService New instance of UserService object. """ headers = self._headers if version is None: version = MIN_API_VERSION if Utils.BoolTypeConvert(self._config['strict']): DfpSanityCheck.ValidateServer(server, version) # Load additional configuration data. op_config = { 'server': server, 'version': version, 'http_proxy': http_proxy } return UserService(headers, self._config, op_config, self.__lock, self.__logger)
def _ReadyCompression(self): """Sets whether the HTTP transport layer should use compression. Overloaded for DFA because the DFA servers do not accept compressed messages. They do support returning compressed messages. """ compress = Utils.BoolTypeConvert(self._config['compress']) self._soappyservice.soapproxy.config.send_compressed = False self._soappyservice.soapproxy.config.accept_compressed = compress
def main(client, order_id): # Initialize appropriate service. line_item_service = client.GetService('LineItemService', version='v201405') # Create statement object to only select line items with even delivery rates. values = [{ 'key': 'deliveryRateType', 'value': { 'xsi_type': 'TextValue', 'value': 'EVENLY' } }, { 'key': 'orderId', 'value': { 'xsi_type': 'NumberValue', 'value': order_id } }] query = 'WHERE deliveryRateType = :deliveryRateType and orderId = :orderId' statement = DfpUtils.FilterStatement(query, values, 500) # Get line items by statement. response = line_item_service.GetLineItemsByStatement( statement.ToStatement())[0] line_items = response.get('results') if line_items: # Update each local line item by changing its delivery rate type. updated_line_items = [] for line_item in line_items: if not Utils.BoolTypeConvert(line_item['isArchived']): line_item['deliveryRateType'] = 'AS_FAST_AS_POSSIBLE' updated_line_items.append(line_item) # Update line items remotely. line_items = line_item_service.UpdateLineItems(updated_line_items) # Display results. if line_items: for line_item in line_items: print( 'Line item with id \'%s\', belonging to order id \'%s\', named ' '\'%s\', and delivery rate \'%s\' was updated.' % (line_item['id'], line_item['orderId'], line_item['name'], line_item['deliveryRateType'])) else: print 'No line items were updated.' else: print 'No line items found to update.'
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 __GenerateHeaders(self, return_micros=None, skip_report_header=None, skip_report_summary=None): """Generates the headers to use for the report download. Args: return_micros: bool whether or not to use micros for money. skip_report_header: A boolean indicating whether to include a header row containing the report name and date range. If false or not specified, report output will include the header row. skip_report_summary: A boolean indicating whether to include a summary row containing the report totals. If false or not specified, report output will include the summary row. Returns: dict Dictionary containing all the headers for the request """ headers = {} if 'clientCustomerId' in self._headers: headers['clientCustomerId'] = self._headers['clientCustomerId'] # Apply OAuth2 headers self._headers['oauth2credentials'].apply(headers) if skip_report_header: headers.update({'skipReportHeader': str(skip_report_header)}) if skip_report_summary: headers.update({'skipReportSummary': str(skip_report_summary)}) if return_micros is not None: if self._op_config[ 'version'] == FINAL_RETURN_MONEY_IN_MICROS_VERSION: headers['returnMoneyInMicros'] = str(return_micros) else: raise AdWordsError( 'returnMoneyInMicros isn\'t supported in this' ' version.') headers['developerToken'] = self._headers['developerToken'] headers['User-Agent'] = self._headers['userAgent'] if Utils.BoolTypeConvert(self._config['compress']): headers['Accept-Encoding'] = 'gzip' headers['User-Agent'] += ',gzip' headers['Content-Encoding'] = 'gzip' return headers
def __init__(self, client_id, client_secret, refresh_token, mcc_cid, dev_token, debug=False): """Initializes an APIHandler. Args: client_id: client_secret: refresh_token: mcc_cid: dev_token: debug: """ credentials = OAuth2Credentials(None, client_id, client_secret, refresh_token, None, self._REFRESH_URL, self._USER_AGENT) credentials.refresh(httplib2.Http()) headers = { 'clientCustomerId': mcc_cid, 'userAgent': self._USER_AGENT, 'developerToken': dev_token, 'validateOnly': 'n', 'partialFailure': 'n', 'oauth2credentials': credentials } # App Engine doesn't allow you to write to the disk. Thus, we need to # explicitly turn off logging. Though, if debugging is enabled, the # data from STDOUT will be redirected to app engine's debugging console. # Note that we also specify that we want to use ElementTree here; see the # README for more information if you would rather use PyXML. config = { 'debug': Utils.BoolTypeConvert(debug), 'xml_log': 'n', 'request_log': 'n', 'pretty_xml': 'n', 'xml_parser': '2', # We're using ElementTree. 'compress': 'n' } self.client = AdWordsClient(headers, config) self.client.use_mcc = True
def main(client, order_id): # Initialize appropriate service. order_service = client.GetService('OrderService', version='v201408') # Create statement object to select a single order by an ID. values = [{ 'key': 'orderId', 'value': { 'xsi_type': 'NumberValue', 'value': order_id } }] query = 'WHERE id = :orderId' statement = DfpUtils.FilterStatement(query, values) # Get orders by statement. response = order_service.GetOrdersByStatement(statement.ToStatement())[0] orders = response.get('results') if orders: # Update each local order object by changing its notes. updated_orders = [] for order in orders: # Archived orders cannot be updated. if not Utils.BoolTypeConvert(order['isArchived']): order['notes'] = 'Spoke to advertiser. All is well.' updated_orders.append(order) # Update orders remotely. orders = order_service.UpdateOrders(updated_orders) # Display results. if orders: for order in orders: print( 'Order with id \'%s\', name \'%s\', advertiser id \'%s\', and ' 'notes \'%s\' was updated.' % (order['id'], order['name'], order['advertiserId'], order['notes'])) else: print 'No orders were updated.' else: print 'No orders found to update.'
def main(client, ad_unit_id, team_id): # Initialize appropriate service. team_service = client.GetService('TeamService', version='v201408') # Create a filter statement to select a single team by ID. values = [{ 'key': 'teamId', 'value': { 'xsi_type': 'NumberValue', 'value': team_id } }] query = 'WHERE id = :teamId' statement = DfpUtils.FilterStatement(query, values) # Get teams by statement. response = team_service.GetTeamsByStatement(statement.ToStatement())[0] teams = response.get('results') if teams: # Update each local team object by adding the ad unit to it. for team in teams: ad_unit_ids = [] if 'adUnitIds' in team: ad_unit_ids = team['adUnitIds'] # Don't add the ad unit if the team has all inventory already. if not Utils.BoolTypeConvert(team['hasAllInventory']): ad_unit_ids.append(ad_unit_id) team['adUnitIds'] = ad_unit_ids # Update teams on the server. teams = team_service.UpdateTeams(teams) # Display results. for team in teams: print('Team with id \'%s\' and name \'%s\' was updated.' % (team['id'], team['name'])) else: print 'No teams found to update.'
def GetService(self, service_name, server='https://advertisersapi.doubleclick.net', version=None, http_proxy=None, op_config=None): """Generic method to create a service. Args: service_name: str Name of the service to create. [optional] server: str API server this object will access. Possible values are: 'https://advertisersapi.doubleclick.net' for production, 'https://advertisersapitest.doubleclick.net' for test, and 'https://betaadvertisersapi.doubleclick.net' for beta. The default behavior is to access the production environment. version: str API version to use. http_proxy: str HTTP proxy to use. op_config: dict Dictionary object with additional configuration values for this operation. Returns: GenericDfaService New object representing the SOAP service. """ if version is None: version = DEFAULT_API_VERSION if Utils.BoolTypeConvert(self._config['strict']): DfaSanityCheck.ValidateServer(server, version) # Load additional configuration data. if op_config is None: op_config = { 'server': server, 'version': version, 'http_proxy': http_proxy } return GenericDfaService(self._headers, self._config, op_config, self.__lock, self.__logger, service_name)
def GetService(self, service_name, server=DFP_SERVER, version=None, http_proxy=None, op_config=None): """Generic method to create a service. Args: service_name: str Name of the service to create. [optional] server: str API server to access for API calls. The default value is 'https://ads.google.com'. version: str API version to use. http_proxy: str HTTP proxy to use. op_config: dict service configuration. Returns: GenericDfpService New object representing the SOAP service. """ if version is None: version = DEFAULT_API_VERSION if 'authToken' in self._headers and version > 'v201311': raise ApiVersionNotSupportedError('ClientLogin is not supported for ' 'version %s, please upgrade to ' 'OAuth2' % version) else: if Utils.BoolTypeConvert(self._config['strict']): DfpSanityCheck.ValidateServer(server, version) # Load additional configuration data. if op_config is None: op_config = { 'server': server, 'version': version, 'http_proxy': http_proxy } return GenericDfpService(self._headers, self._config, op_config, self.__lock, self.__logger, service_name)
def __GenerateHeaders(self, skip_report_header=None, skip_report_summary=None): """Generates the headers to use for the report download. Args: skip_report_header: A boolean indicating whether to include a header row containing the report name and date range. If false or not specified, report output will include the header row. skip_report_summary: A boolean indicating whether to include a summary row containing the report totals. If false or not specified, report output will include the summary row. Returns: dict Dictionary containing all the headers for the request """ headers = {} if 'clientCustomerId' in self._headers: headers['clientCustomerId'] = self._headers['clientCustomerId'] # Apply OAuth2 headers self._headers['oauth2credentials'].apply(headers) if skip_report_header: headers.update({'skipReportHeader': str(skip_report_header)}) if skip_report_summary: headers.update({'skipReportSummary': str(skip_report_summary)}) headers['developerToken'] = self._headers['developerToken'] headers['User-Agent'] = self._headers['userAgent'] if Utils.BoolTypeConvert(self._config['compress']): headers['Accept-Encoding'] = 'gzip' headers['User-Agent'] += ',gzip' headers['Content-Encoding'] = 'gzip' return headers
# Initialize appropriate service. By default, the request is always made against # the sandbox environment. order_service = client.GetOrderService('https://sandbox.google.com', 'v201004') # Create statement object to get all orders. filter_statement = {'query': 'LIMIT 500'} # Get orders by statement. orders = order_service.GetOrdersByStatement(filter_statement)[0]['results'] if orders: # Update each local order object by changing its notes. updated_orders = [] for order in orders: # Archived orders cannot be updated. if not Utils.BoolTypeConvert(order['isArchived']): order['notes'] = 'Spoke to advertiser. All is well.' updated_orders.append(order) # Update orders remotely. orders = order_service.UpdateOrders(updated_orders) # Display results. if orders: for order in orders: print( 'Order with id \'%s\', name \'%s\', advertiser id \'%s\', and ' 'notes \'%s\' was updated.' % (order['id'], order['name'], order['advertiserId'], order['notes'])) else:
def __init__(self, headers=None, config=None, path=None): """Inits Client. Args: [optional] headers: dict Object with populated authentication credentials. config: dict Object with client configuration values. path: str Relative or absolute path to home directory (i.e. location of pickles and logs/). Example: headers = { 'email': '*****@*****.**', 'password': '******', 'authToken': '...', 'applicationName': 'GoogleTest', 'networkCode': 'ca-01234567', 'oauth2credentials': 'See use_oauth2.py' } config = { 'home': '/path/to/home', 'log_home': '/path/to/logs/home', 'proxy': 'http://example.com:8080', 'xml_parser': '1', # PYXML = 1, ELEMENTREE = 2 'debug': 'n', 'raw_debug': 'n', 'xml_log': 'y', 'request_log': 'y', 'raw_response': 'n', 'strict': 'y', 'pretty_xml': 'y', 'compress': 'y', 'access': '' } path = '/path/to/home' """ super(DfpClient, self).__init__(headers, config, path) self.__lock = thread.allocate_lock() self.__loc = None if path is not None: # Update absolute path for a given instance of DfpClient, based on # provided relative path. if os.path.isabs(path): DfpClient.home = path else: # NOTE(api.sgrinberg): Keep first parameter of join() as os.getcwd(), # do not change it to DfpClient.home. Otherwise, may break when # multiple instances of DfpClient exist during program run. DfpClient.home = os.path.join(os.getcwd(), path) # If pickles don't exist at given location, default to "~". if (not headers and not config and (not os.path.exists(os.path.join(DfpClient.home, DfpClient.auth_pkl_name)) or not os.path.exists(os.path.join(DfpClient.home, DfpClient.config_pkl_name)))): DfpClient.home = os.path.expanduser('~') elif not headers: DfpClient.home = os.path.expanduser('~') # Update location for both pickles. DfpClient.auth_pkl = os.path.join(DfpClient.home, DfpClient.auth_pkl_name) DfpClient.config_pkl = os.path.join(DfpClient.home, DfpClient.config_pkl_name) # Only load from the pickle if config wasn't specified. self._config = config or self.__LoadConfigValues() self._config = self.__SetMissingDefaultConfigValues(self._config) self._config['home'] = DfpClient.home # Validate XML parser to use. SanityCheck.ValidateConfigXmlParser(self._config['xml_parser']) # Only load from the pickle if 'headers' wasn't specified. if headers is None: self._headers = self.__LoadAuthCredentials() else: if Utils.BoolTypeConvert(self._config['strict']): SanityCheck.ValidateRequiredHeaders(headers, REQUIRED_SOAP_HEADERS) self._headers = headers # Load/set authentication token. try: if headers and 'authToken' in headers and headers['authToken']: self._headers['authToken'] = headers['authToken'] elif 'email' in self._headers and 'password' in self._headers: self._headers['authToken'] = Utils.GetAuthToken( self._headers['email'], self._headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, self._config['proxy']) elif (self._headers.get('oauth2credentials')): # If they have oauth2credentials, that's also fine. pass else: msg = ('Authentication data, email or/and password, OAuth2 credentials ' 'is missing.') raise ValidationError(msg) self._config['auth_token_epoch'] = time.time() except AuthTokenError: # We would end up here if non-valid Google Account's credentials were # specified. self._headers['authToken'] = None self._config['auth_token_epoch'] = 0 # Initialize logger. self.__logger = Logger(LIB_SIG, self._config['log_home'])
def CallMethod(self, method_name, params, service_name=None, loc=None, request=None): """Make an API call to specified method. Args: method_name: str API method name. params: list List of parameters to send to the API method. [optional] service_name: str API service name. loc: service Locator. request: instance Holder of the SOAP request. Returns: tuple/str Response from the API method. If 'raw_response' flag enabled a string is returned, tuple otherwise. """ # Acquire thread lock. self._lock.acquire() try: if not service_name and self.__service: service_name = self.__service headers = self._headers config = self._config config['data_injects'] = () error = {} # Load/unload version specific authentication and configuration data. if AdWordsSanityCheck.IsJaxbApi(self._op_config['version']): # Set boolean to the format expected by the server, True => true. for key in ['validateOnly', 'partialFailure']: if key in self._headers: self._headers[key] = self._headers[key].lower() # Load/set authentication token. If authentication token has expired, # regenerate it. now = time.time() if ((('authToken' not in headers and 'auth_token_epoch' not in config) or int(now - config['auth_token_epoch']) >= AUTH_TOKEN_EXPIRE) and not 'oauth_enabled' in config): if ('email' not in headers or not headers['email'] or 'password' not in headers or not headers['password']): msg = ( 'Required authentication headers, \'email\' and ' '\'password\', are missing. Unable to regenerate ' 'authentication token.') raise ValidationError(msg) headers['authToken'] = Utils.GetAuthToken( headers['email'], headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, config['proxy']) config['auth_token_epoch'] = time.time() self._headers = headers self._config = config headers = Utils.UnLoadDictKeys( headers, ['email', 'password', 'useragent']) ns = '/'.join([ 'https://adwords.google.com/api/adwords', self._op_config['group'], self._op_config['version'] ]) default_ns = '/'.join([ 'https://adwords.google.com/api/adwords', self._op_config['default_group'], self._op_config['version'] ]) config['ns_target'] = (ns, 'RequestHeader') if (config['soap_lib'] == SOAPPY and (self._op_config['default_group'] != self._op_config['group'] or self.__service == 'BulkMutateJobService')): from adspygoogle.adwords.soappy import SERVICE_TYPES data_injects = [] for header in headers: if headers[header]: data_injects.append((header, 'ns1:%s' % header)) data_injects.append( ('<RequestHeader>', '<RequestHeader xmlns="%s" xmlns:ns1="%s">' % (ns, default_ns))) data_injects.append( ('<SOAP-ENV:Body xmlns="%s">' % ns, '<SOAP-ENV:Body xmlns="%s" xmlns:ns1="%s">' % (ns, default_ns))) for item in SERVICE_TYPES: if (item['group'] == self._op_config['default_group'] and re.compile( '<%s>|<%s ' % (item['attr'], item['attr'])).findall(params)): # TODO(api.sgrinberg): Find a more elegant solution that doesn't # involve manual triggering for attributes that show up in both # groups. if ((self._op_config['group'] == 'o' and item['attr'] == 'urls') or (self.__service == 'TrafficEstimatorService' and item['attr'] == 'maxCpc') or item['attr'] == 'selector' or item['attr'] == 'operator'): continue if self.__service != 'BulkMutateJobService': data_injects.append( (' xsi3:type="%s">' % item['type'], '>')) data_injects.append(('<%s>' % item['attr'], '<ns1:%s>' % item['attr'])) data_injects.append(('<%s ' % item['attr'], '<ns1:%s ' % item['attr'])) data_injects.append(('</%s>' % item['attr'], '</ns1:%s>' % item['attr'])) config['data_injects'] = tuple(data_injects) else: headers['useragent'] = headers['userAgent'] headers = Utils.UnLoadDictKeys(headers, ['authToken', 'userAgent']) config = Utils.UnLoadDictKeys( config, ['ns_target', 'auth_token_epoch']) buf = AdWordsSoapBuffer(xml_parser=self._config['xml_parser'], pretty_xml=Utils.BoolTypeConvert( self._config['pretty_xml'])) start_time = time.strftime('%Y-%m-%d %H:%M:%S') response = super(AdWordsWebService, self).CallMethod( headers, config, method_name, params, buf, AdWordsSanityCheck.IsJaxbApi(self._op_config['version']), LIB_SIG, LIB_URL, service_name, loc, request) stop_time = time.strftime('%Y-%m-%d %H:%M:%S') # Restore list type which was overwritten by SOAPpy. if config['soap_lib'] == SOAPPY and isinstance(response, tuple): from adspygoogle.common.soappy import MessageHandler holder = [] for element in response: holder.append( MessageHandler.RestoreListType( element, ('value', 'partialFailureErrors', 'conversionTypes'))) response = tuple(holder) if isinstance(response, dict) or isinstance(response, Error): error = response if not Utils.BoolTypeConvert(self.__config['raw_debug']): self.__ManageSoap(buf, start_time, stop_time, error) finally: # Release thread lock. if self._lock.locked(): self._lock.release() if Utils.BoolTypeConvert(self._config['raw_response']): return response return response
filter_statement = {'query': 'WHERE id > 0 LIMIT 5'} # Get teams by statement. response = team_service.GetTeamsByStatement(filter_statement)[0] teams = [] if 'results' in response: teams = response['results'] if teams: # Update each local team object by adding the ad unit to it. for team in teams: ad_unit_ids = [] if 'adUnitIds' in team: ad_unit_ids = team['adUnitIds'] # Don't add the ad unit if the team has all inventory already. if not Utils.BoolTypeConvert(team['hasAllInventory']): ad_unit_ids.append(ad_unit_id) team['adUnitIds'] = ad_unit_ids # Update teams on the server. teams = team_service.UpdateTeams(teams) # Display results. if teams: for team in teams: print('Team with id \'%s\' and name \'%s\' was updated.' % (team['id'], team['name'])) else: print 'No teams were updated.' else: print 'No teams found to update.'
}] filter_statement = {'query': 'WHERE deliveryRateType = :deliveryRateType and ' 'orderId = :orderId LIMIT 500', 'values': values} # Get line items by statement. response = line_item_service.GetLineItemsByStatement(filter_statement)[0] line_items = [] if 'results' in response: line_items = response['results'] if line_items: # Update each local line item by changing its delivery rate type. updated_line_items = [] for line_item in line_items: if not Utils.BoolTypeConvert(line_item['isArchived']): line_item['deliveryRateType'] = 'AS_FAST_AS_POSSIBLE' updated_line_items.append(line_item) # Update line items remotely. line_items = line_item_service.UpdateLineItems(updated_line_items) # Display results. if line_items: for line_item in line_items: print ('Line item with id \'%s\', belonging to order id \'%s\', named ' '\'%s\', and delivery rate \'%s\' was updated.' % (line_item['id'], line_item['orderId'], line_item['name'], line_item['deliveryRateType'])) else: print 'No line items were updated.'