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:
- 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]
- 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