I want to remove a few ads in one server request. What is the difference between:

  • batch-processing (only async?)
  • bulk mutates (only sync, shorter code?)

I have tried both ways and got errors:

  1. batch-processing

I've tried to follow this post about batch-processing to create an async batch job for removing multiple ads. It was sent to the server, but I didn't see the sent ad ids were deleted.

Do I miss anything?

class ServiceWrapper:     """Wraps GoogleAdsService API request"""     # public properties ...     def __init__(self, client, customer_id):         self._client = client         self._ga_service = client.get_service("GoogleAdsService")         self._ad_group_ad_service = client.get_service("AdGroupAdService")         self._batch_job_service = client.get_service("BatchJobService")         self._customer_id = customer_id         self._batch_job_operation = self._create_batch_job_operation(client)         self._batch_job_resource_name = self._create_batch_job(self._batch_job_service, customer_id,                                                                self._batch_job_operation)     def _create_batch_job_operation(self, client):         """Created a BatchJobOperation and sets an empty BatchJob instance to         the "create" property in order to tell the Google Ads API that we're         creating a new BatchJob.         Args:             client: an initialized GoogleAdsClient instance.         Returns: a BatchJobOperation with a BatchJob instance set in the "create"             property.         """         batch_job_operation = client.get_type("BatchJobOperation")         batch_job = client.get_type("BatchJob")         client.copy_from(batch_job_operation.create, batch_job)         return batch_job_operation     def _create_batch_job(self, batch_job_service, customer_id, batch_job_operation):         """Creates a batch job for the specified customer ID.         Args:             batch_job_service: an instance of the BatchJobService message class.             customer_id: a str of a customer ID.             batch_job_operation: a BatchJobOperation instance set to "create"         Returns: a str of a resource name for a batch job.         """         try:             response = batch_job_service.mutate_batch_job(                 customer_id=customer_id, operation=batch_job_operation             )             resource_name = response.result.resource_name             print(f'Created a batch job with resource name "{resource_name}"')             return resource_name         except GoogleAdsException as exception:             handle_googleads_exception(exception)     def add_all_batch_job_operations(self, batch_job_service, operations, resource_name):         """Adds all mutate operations to the batch job.         As this is the first time for this batch job, we pass null as a sequence         token. The response will contain the next sequence token that we can use         to upload more operations in the future.         Args:             batch_job_service: an instance of the BatchJobService message class.             operations: a list of a mutate operations.             resource_name: a str of a resource name for a batch job.         """         try:             response = batch_job_service.add_batch_job_operations(                 resource_name=resource_name,                 sequence_token=None,                 mutate_operations=operations,             )             print(                 f"{response.total_operations} mutate operations have been "                 "added so far."             )             # You can use this next sequence token for calling             # add_batch_job_operations() next time.             print(                 "Next sequence token for adding next operations is "                 f"{response.next_sequence_token}"             )         except GoogleAdsException as exception:             handle_googleads_exception(exception) def remove_disapproved_ads_for_account(account):     """Remove all disapproved ads for a given customer id"""     ad_removal_operations = []         for row in rows:                     ad_removal_operations.append(                         build_removal_operation(customer_id, ad_json["ad_group_id"],          if len(ad_removal_operations) > 0:         remove_ads(ad_removal_operations)         #serviceWrapper.mutate(customer_id, [mutate_operation1, mutate_operation2]) def build_removal_operation(customer_id, ad_group_id, ad_id):     """Removes the specified ad"""     resource_name = serviceWrapper.ad_group_ad_service.ad_group_ad_path(         customer_id, ad_group_id, ad_id     )     ad_group_ad_operation = serviceWrapper.client.get_type("AdGroupAdOperation")     ad_group_ad_operation.remove = resource_name     return ad_group_ad_operation async def remove_ads(removal_operations):     """Removes the specified ad"""     serviceWrapper.add_all_batch_job_operations(serviceWrapper.batch_job_service, removal_operations,                                                 serviceWrapper.batch_job_resource_name)     operations_response = _run_batch_job(serviceWrapper.batch_job_service, serviceWrapper.batch_job_resource_name)     # Create an asyncio.Event instance to control execution during the     # asyncronous steps in _poll_batch_job. Note that this is not important     # for polling asyncronously, it simply helps with execution control so we     # can run _fetch_and_print_results after the asyncronous operations have     # completed.     _done_event = asyncio.Event()     _poll_batch_job(operations_response, _done_event)     # Execution will stop here and wait for the asyncronous steps in     # _poll_batch_job to complete before proceeding.     await _done_event.wait()     _fetch_and_print_results(serviceWrapper.client, serviceWrapper.batch_job_service,                              serviceWrapper.batch_job_resource_name) def _run_batch_job(batch_job_service, resource_name):     """Runs the batch job for executing all uploaded mutate operations.     Args:         batch_job_service: an instance of the BatchJobService message class.         resource_name: a str of a resource name for a batch job.     Returns: a google.api_core.operation.Operation instance.     """     try:         response = batch_job_service.run_batch_job(resource_name=resource_name)         print(             f'Batch job with resource name "{resource_name}" has been '             "executed."         )         return response     except GoogleAdsException as exception:         handle_googleads_exception(exception) def _poll_batch_job(operations_response, event):     """Polls the server until the batch job execution finishes.     Sets the initial poll delay time and the total time to wait before time-out.     Args:         operations_response: a google.api_core.operation.Operation instance.         event: an instance of asyncio.Event to invoke once the operations have             completed, alerting the awaiting calling code that it can proceed.     """     loop = asyncio.get_event_loop()     def _done_callback(future):         # The operations_response object will call callbacks from a daemon         # thread so we must use a threadsafe method of setting the event here         # otherwise it will not trigger the awaiting code.         loop.call_soon_threadsafe(event.set)     # operations_response represents a Long-Running Operation or LRO. The class     # provides an interface for polling the API to check when the operation is     # complete. Below we use the asynchronous interface, but there's also a     # synchronous interface that uses the Operation.result method.     # See: https://googleapis.dev/python/google-api-core/latest/operation.html     operations_response.add_done_callback(_done_callback) def _fetch_and_print_results(client, batch_job_service, resource_name):     """Prints all the results from running the batch job.     Args:         client: an initialized GoogleAdsClient instance.         batch_job_service: an instance of the BatchJobService message class.         resource_name: a str of a resource name for a batch job.     """     print(         f'Batch job with resource name "{resource_name}" has finished. '         "Now, printing its results..."     )     list_results_request = client.get_type("ListBatchJobResultsRequest")     list_results_request.resource_name = resource_name     list_results_request.page_size = BULK_REMOVE_PAGE_SIZE     # Gets all the results from running batch job and prints their information.     batch_job_results = batch_job_service.list_batch_job_results(         request=list_results_request     )     for batch_job_result in batch_job_results:         status = batch_job_result.status.message         status = status if status else "N/A"         result = batch_job_result.mutate_operation_response         result = result or "N/A"         print(             f"Batch job #{batch_job_result.operation_index} "             f'has a status "{status}" and response type "{result}"'         )         # [END add_complete_campaigns_using_batch_job_4] 
  1. Bulk Mutates

If I choose to follow this post about Bulk Mutates, and create a sync batch, I get an undefined symbol :Mutate how can I fix this? Or make this code work?

class ServiceWrapper:     """Wraps GoogleAdsService API request"""     # public properties ...     def __init__(self, client, customer_id):         self._client = client         self._ga_service = client.get_service("GoogleAdsService")         self._ad_group_ad_service = client.get_service("AdGroupAdService")         self._batch_job_service = client.get_service("BatchJobService")         self._customer_id = customer_id         self._batch_job_operation = self._create_batch_job_operation(client)         self._batch_job_resource_name = self._create_batch_job(self._batch_job_service, customer_id,                                                                self._batch_job_operation)          def build_removal_operation_sync(customer_id, ad_group_id, ad_id):      mutate_operation1 = serviceWrapper.client.operation(:Mutate)      """Removes the specified ad"""      resource_name = serviceWrapper.ad_group_ad_service.ad_group_ad_path(          customer_id, ad_group_id, ad_id      )      ad_group_ad_operation = serviceWrapper.client.get_type("AdGroupAdOperation")      ad_group_ad_operation.remove = resource_name      mutate_operation1.ad_group_ad_operation = campaign_operation 

Tag:google-ads-api, python-3.x, ads-api, google-api-python-client, asynchronous

Add a new comment.