Você já se perguntou: "Se eu alterar a página de destino dos meus anúncios, terei mais conversões?" ou "E se eu reformular meu anúncio dessa forma? Ajudaria a gerar tráfego?"? A configuração de um teste para isolar esse tipo de variável específica pode ser monótona, mas usando rascunhos e experiências de campanha, todo o trabalho pesado (cópia de dados, configuração etc.) é feito por você.
É fácil e rápido configurar novas experiências de campanha por meio do DraftService e do TrialService. Veja as etapas a serem seguidas para a configuração:
- Crie um rascunho com base em uma campanha existente. Rascunho é um espelho de uma campanha existente que não veicula anúncios.
- Modifique a campanha de rascunho de acordo com a necessidade da sua experiência.
- Crie um teste do seu rascunho. O teste concede acesso a uma nova campanha de teste que começa com uma cópia da campanha de rascunho e divide o tráfego entre a campanha de teste e a campanha base. Essas campanhas de teste também são conhecidas como experiências.
- Compare as estatísticas da sua campanha de teste com as da campanha base para ver qual tem o melhor desempenho.
Essas etapas representam o fluxo de trabalho mais comum, mas o DraftService e o TrialService são flexíveis e podem ser usados de outras formas. Por exemplo, é possível usar um rascunho para reproduzir as alterações em uma campanha base e depois integrá-las novamente na campanha sem usar um teste. Se você não usa um teste e gosta do desempenho, tem a opção de promover seus atributos de volta na campanha base ou graduá-los em uma campanha desenvolvida junto à campanha base.
Este fluxograma mostra os fluxos de trabalho que podem ser empregados usando rascunhos e experiências de campanha:
Rascunhos
Rascunho é um objeto que mantém a associação entre uma campanha de rascunho e uma campanha base. Ele é criado no DraftService fornecendo o ID de uma campanha existente que servirá como base. O DraftService cria automaticamente uma nova campanha de rascunho e a associa com o rascunho. Um objeto "Rascunho" não é, por si só, a campanha de rascunho. Ele simplesmente relaciona uma campanha de rascunho à sua campanha base. A campanha de rascunho existe como um objeto Campanha real. O campaignId
da campanha de rascunho está disponível como draftCampaignId
do rascunho. É possível modificar a campanha de rascunho como faria com uma campanha real, como alterar seus critérios, grupos de anúncios, lances e anúncios. No entanto, uma campanha de rascunho não veicula anúncios.
Para que uma campanha existente sirva como base para um rascunho, ela precisa atender a alguns requisitos, como ser uma campanha da rede de pesquisa, da rede de pesquisa com exibição em Display ou da Rede de Display (exceto uma campanha de aplicativo para dispositivos móveis para a Rede de Display) e ter um orçamento não compartilhado (o campo isExplicitlyShared no orçamento da campanha precisa estar definido como falso). Embora as experiências sejam compatíveis com grande parte dos recursos de campanhas, há algumas exceções.
Como criar um rascunho
Para criar um rascunho, defina um baseCampaignId
e nomeie o rascunho. O nome precisa ser diferente de todos os rascunhos da sua conta.
Veja um exemplo:
Java
// Get the DraftService. DraftServiceInterface draftService = adWordsServices.get(session, DraftServiceInterface.class); Draft draft = new Draft(); draft.setBaseCampaignId(baseCampaignId); draft.setDraftName("Test Draft #" + System.currentTimeMillis()); DraftOperation draftOperation = new DraftOperation(); draftOperation.setOperator(Operator.ADD); draftOperation.setOperand(draft); draft = draftService.mutate(new DraftOperation[] {draftOperation}).getValue(0);
VB
Dim draft As New Draft() draft.baseCampaignId = baseCampaignId draft.draftName = "Test Draft #" + ExampleUtilities.GetRandomString() Dim draftOperation As New DraftOperation() draftOperation.operator = [Operator].ADD draftOperation.operand = draft
C#
Draft draft = new Draft() { baseCampaignId = baseCampaignId, draftName = "Test Draft #" + ExampleUtilities.GetRandomString() }; DraftOperation draftOperation = new DraftOperation() { @operator = Operator.ADD, operand = draft };
PHP
$draftService = $adWordsServices->get($session, DraftService::class); $operations = []; // Create a draft. $draft = new Draft(); $draft->setBaseCampaignId($baseCampaignId); $draft->setDraftname('Test Draft #' . uniqid()); // Create a draft operation and add it to the operations list. $operation = new DraftOperation(); $operation->setOperand($draft); $operation->setOperator(Operator::ADD); $operations[] = $operation; // Create the draft on the server and print out some information for // the created draft. $result = $draftService->mutate($operations); $draft = $result->getValue()[0];
Perl
my $draft = Google::Ads::AdWords::v201802::Draft->new({ baseCampaignId => $base_campaign_id, draftName => sprintf("Test Draft #%s", uniqid())}); # Create operation. my $draft_operation = Google::Ads::AdWords::v201802::DraftOperation->new({ operator => "ADD", operand => $draft });
Python
draft_service = client.GetService('DraftService', version='v201802') draft = { 'baseCampaignId': base_campaign_id, 'draftName': 'Test Draft #%s' % uuid.uuid4() } draft_operation = {'operator': 'ADD', 'operand': draft} draft = draft_service.mutate([draft_operation])['value'][0] draft_campaign_id = draft['draftCampaignId']
Ruby
draft_srv = adwords.service(:DraftService, API_VERSION) draft = { :base_campaign_id => base_campaign_id, :draft_name => 'Test Draft #%d' % (Time.new.to_f * 1000).to_i } draft_operation = {:operator => 'ADD', :operand => draft} draft_result = draft_srv.mutate([draft_operation]) draft = draft_result[:value].first draft_id = draft[:draft_id] draft_campaign_id = draft[:draft_campaign_id]
O rascunho inclui detalhes como draftCampaignId
, draftId
e baseCampaignId
, todos importantes. draftCampaignId
é usado para modificar a campanha de rascunho e seus grupos de anúncios, critérios e anúncios. draftId
e baseCampaignId
servem como referência para o rascunho ao criar um teste ou promover o rascunho.
Personalização da campanha de rascunho
O campo draftCampaignId
pode ser usado como código da campanha real usando qualquer serviço que possa precisar de um código (CampaignService, AdGroupService, CampaignCriterionService etc.). Por exemplo, para adicionar um novo critério de idioma à campanha, basta usar o draftCampaignId
do rascunho como seu campaignId
:
Java
CampaignCriterionServiceInterface campaignCriterionService = adWordsServices.get(session, CampaignCriterionServiceInterface.class); Language language = new Language(); language.setId(1003L); // Spanish // Make sure to use the draftCampaignId when modifying the virtual draft campaign. CampaignCriterion campaignCriterion = new CampaignCriterion(); campaignCriterion.setCampaignId(draft.getDraftCampaignId()); campaignCriterion.setCriterion(language); CampaignCriterionOperation criterionOperation = new CampaignCriterionOperation(); criterionOperation.setOperator(Operator.ADD); criterionOperation.setOperand(campaignCriterion); campaignCriterion = campaignCriterionService .mutate(new CampaignCriterionOperation[] {criterionOperation}) .getValue(0);
VB
campaign_criterion_srv = adwords.service(:CampaignCriterionService, API_VERSION) criterion = { :xsi_type => 'Language', :id => 1003 # Spanish } criterion_operation = { # Make sure to use the draft_campaign_id when modifying the virtual draft # campaign. :operator => 'ADD', :operand => { :campaign_id => draft_campaign_id, :criterion => criterion } } criterion_result = campaign_criterion_srv.mutate([criterion_operation])
C#
Language language = new Language() { id = 1003L // Spanish }; // Make sure to use the draftCampaignId when modifying the virtual draft // campaign. CampaignCriterion campaignCriterion = new CampaignCriterion() { campaignId = draft.draftCampaignId, criterion = language }; CampaignCriterionOperation criterionOperation = new CampaignCriterionOperation() { @operator = Operator.ADD, operand = campaignCriterion }; campaignCriterion = campaignCriterionService.mutate( new CampaignCriterionOperation[] { criterionOperation }).value[0];
PHP
$campaignCriterionService = $adWordsServices->get($session, CampaignCriterionService::class); // Create a criterion. $language = new Language(); $language->setId(1003); // Spanish $campaignCriterion = new CampaignCriterion(); $campaignCriterion->setCampaignId($draft->getDraftCampaignId()); $campaignCriterion->setCriterion($language); // Create a campaign criterion operation and add it to the operations list. $operations = []; $operation = new CampaignCriterionOperation(); $operation->setOperand($campaignCriterion); $operation->setOperator(Operator::ADD); $operations[] = $operation; // Create a campaign criterion on the server. $campaignCriterionService->mutate($operations);
Perl
my $criterion = Google::Ads::AdWords::v201802::Language->new({ id => 1003 # Spanish }); my $operation = Google::Ads::AdWords::v201802::CampaignCriterionOperation->new({ operator => "ADD", operand => Google::Ads::AdWords::v201802::CampaignCriterion->new({ campaignId => $draft_campaign_id, criterion => $criterion })}); $result = $client->CampaignCriterionService()->mutate({operations => [$operation]}); $criterion = $result->get_value()->[0];
Python
campaign_criterion_service = client.GetService('CampaignCriterionService', version='v201802') criterion = { 'xsi_type': 'Language', 'id': 1003 # Spanish } criterion_operation = { # Make sure to use the draftCampaignId when modifying the virtual draft # campaign. 'operator': 'ADD', 'operand': { 'campaignId': draft_campaign_id, 'criterion': criterion } } criterion = campaign_criterion_service.mutate([criterion_operation])[ 'value'][0]
Ruby
campaign_criterion_srv = adwords.service(:CampaignCriterionService, API_VERSION) criterion = { :xsi_type => 'Language', :id => 1003 # Spanish } criterion_operation = { # Make sure to use the draft_campaign_id when modifying the virtual draft # campaign. :operator => 'ADD', :operand => { :campaign_id => draft_campaign_id, :criterion => criterion } } criterion_result = campaign_criterion_srv.mutate([criterion_operation])
Operações semelhantes podem ser realizadas em grupos de anúncios da campanha de rascunho. Por exemplo, é possível buscar grupos de anúncios de uma campanha de rascunho usando um filtro:
Java
// Get the AdGroupService. AdGroupServiceInterface adGroupService = adWordsServices.get(session, AdGroupServiceInterface.class); // Create a selector that limits to ad groups in the draft campaign. Selector selector = new SelectorBuilder() .fields(AdGroupField.Id) .equals(AdGroupField.CampaignId, Long.toString(draftCampaignId)) .limit(100) .build(); // Make a 'get' request. AdGroupPage adGroupPage = adGroupService.get(selector); // Display the results. if (adGroupPage.getEntries() != null && adGroupPage.getEntries().length > 0) { System.out.printf( "Found %d of %d ad groups.%n", adGroupPage.getEntries().length, adGroupPage.getTotalNumEntries()); } else { System.out.println("No ad groups found."); }
VB
' Get the AdGroupService. Dim adGroupService As AdGroupService = CType(user.GetService( AdWordsService.v201802.AdGroupService), AdGroupService) ' Create a selector that limits to ad groups in the draft campaign. Dim selector As New Selector selector.fields = New String() { AdGroup.Fields.Id } selector.predicates = New Predicate() { Predicate.Equals(AdGroup.Fields.CampaignId, draftCampaignId) } selector.paging = Paging.Default Dim page As AdGroupPage = adGroupService.get(selector) ' Display the results. If ((Not page Is Nothing) AndAlso (Not page.entries Is Nothing)) Then Console.WriteLine("Fetched {0} of {1} ad groups.", page.entries.Length, page.totalNumEntries) End If
PHP
$adGroupService = $adWordsServices->get($session, AdGroupService::class); // Create a selector to select all ad groups for the specified draft // campaign. $selector = new Selector(); $selector->setFields(['Id']); $selector->setPredicates( [ new Predicate( 'CampaignId', PredicateOperator::EQUALS, [$draftCampaignId] ) ] ); $selector->setPaging(new Paging(0, self::PAGE_LIMIT)); // Retrieve ad groups for the specified draft campaign. $page = $adGroupService->get($selector); // Print out some information for the ad groups. if ($page->getTotalNumEntries() > 0) { printf("Found %d ad groups.\n", $page->getTotalNumEntries()); } else { print "No ad groups were found.\n"; }
Perl
# Create predicates. my $campaign_predicate = Google::Ads::AdWords::v201802::Predicate->new({ field => "CampaignId", operator => "EQUALS", values => [$draft_campaign_id]}); # Create selector. my $paging = Google::Ads::AdWords::v201802::Paging->new({ startIndex => 0, numberResults => PAGE_SIZE }); my $selector = Google::Ads::AdWords::v201802::Selector->new({ fields => ["Id"], predicates => [$campaign_predicate], paging => $paging }); my $ad_group_page = $client->AdGroupService()->get({ serviceSelector => $selector }); if ($ad_group_page->get_entries()) { printf( "Found %d of %d ad groups.\n", scalar(@{$ad_group_page->get_entries()}), $ad_group_page->get_totalNumEntries()); } else { printf("No ad groups found.\n"); }
Python
ad_group_service = client.GetService('AdGroupService', version='v201802') selector = { 'fields': ['Id'], 'paging': { 'startIndex': str(0), 'numberResults': str(PAGE_SIZE) }, 'predicates': [{ 'field': 'CampaignId', 'operator': 'IN', 'values': [draft_campaign_id] }] } response = ad_group_service.get(selector) draft_ad_groups = response['entries'] if 'entries' in response else []
Ruby
ad_group_srv = adwords.service(:AdGroupService, API_VERSION) selector = { :fields => ['Id'], :predicates => [{ :field => 'CampaignId', :operator => 'IN', :values => [draft_campaign_id] }], :paging => { :start_index => 0, :number_results => 100 } } ad_group_page = ad_group_srv.get(selector) unless ad_group_page[:entries].nil? puts "Found %d of %d ad groups." % [ad_group_page[:entries].size, ad_group_page[:total_num_entries]] else puts "No ad groups found." end
São realizadas verificações de política em anúncios de campanhas de rascunho, assim como em campanhas normais. Violações da política ocorridas somente após a veiculação do anúncio não resultarão em outros anúncios de rascunho, mas em um erro se e quando um teste desse rascunho for executado.
Quando terminar de fazer as alterações na campanha de rascunho, você terá duas opções:
- Promover as alterações da campanha de rascunho diretamente na campanha base.
- Criar uma campanha de teste para ser exibida ao mesmo tempo que campanha base.
Promoção da campanha de rascunho
É possível usar rascunhos como uma simples área de teste para alterações pendentes da sua campanha real sem criar um teste. Quando todas as alterações forem testadas na campanha de rascunho, bastará promover o rascunho, fazendo com que essas alterações sejam aplicadas à campanha base. Para promover um rascunho, envie uma operação de mutação que define o status para PROMOTING
. Como isso envolve a cópia de todas as alterações da campanha de rascunho de volta na campanha base, a promoção é realizada de forma assíncrona e é irreversível. A configuração do status inicia o processo assíncrono.
É possível pesquisar o rascunho usando draftId
e baseCampaignId
para monitorar o campo de status. Quando o status muda de PROMOTING
, a operação está concluída.
PROMOTED
indica sucesso e PROMOTE_FAILED
indica que um erro foi encontrado.
Falaremos mais sobre esquemas de pesquisa na seção Testes.
Se ocorrer um erro durante a promoção do rascunho, envie o draftId
e o baseCampaignId
ao DraftAsyncErrorService para ver mais detalhes sobre erros específicos.
Consulte a seção Erros para ver um exemplo.
Campanhas de rascunho são identificáveis porque o campaignTrialType
delas é DRAFT
. Também é possível pesquisar o ID da campanha base em uma campanha de rascunho usando o campo baseCampaignId
. Para campanhas normais (não criadas com um rascunho ou teste), o campaignTrialType
é BASE
e o campo baseCampaignId
é o próprio ID da campanha.
Por padrão, entidades de rascunho não são incluídas nos resultados gerados de outros serviços, como CampaignService ou AdGroupService. Para buscar uma entidade de rascunho, como uma campanha de rascunho ou um grupo de anúncios de rascunho, é necessário especificar o campaignTrialType
explicitamente como DRAFT
nos atributos ou filtrar um ID de uma entidade de rascunho conhecida, como draftCampaignId
.
Testes
Teste é uma entidade que ajuda a gerenciar uma campanha de teste sendo exibida junto a uma campanha base, com parte do tráfego e orçamento, para testar o que é configurado no rascunho. Um teste produz uma campanha em tempo real (também denominada experiência) que veicula anúncios como uma campanha normal. Ele coleta estatísticas separadamente da sua campanha base. Entretanto, um teste é considerado no limite do número de campanhas, grupos de anúncios etc. que você pode ter na sua conta em dado momento.
Criação de um teste
Para criar um teste, forneça o draftId
e o baseCampaignId
para identificar o rascunho com base no qual você está começando o teste, um nome exclusivo e a porcentagem do tráfego que deseja gerar no teste. A criação de um teste automaticamente cria uma campanha de teste recém-associada.
Testes são identificados exclusivamente pelo seu código, ao contrário de rascunhos. Portanto, quando um teste é criado, não é necessário usar o baseCampaignId
para se referir a ele.
Java
// Get the TrialService. TrialServiceInterface trialService = adWordsServices.get(session, TrialServiceInterface.class); Trial trial = new Trial(); trial.setDraftId(draftId); trial.setBaseCampaignId(baseCampaignId); trial.setName("Test Trial #" + System.currentTimeMillis()); trial.setTrafficSplitPercent(50); TrialOperation trialOperation = new TrialOperation(); trialOperation.setOperator(Operator.ADD); trialOperation.setOperand(trial); long trialId = trialService.mutate(new TrialOperation[] {trialOperation}).getValue(0).getId();
VB
Dim newTrial As New Trial newTrial.draftId = draftId newTrial.baseCampaignId = baseCampaignId newTrial.name = "Test Trial #" & ExampleUtilities.GetRandomString() newTrial.trafficSplitPercent = 50 Dim trialOperation As New TrialOperation() trialOperation.operator = [Operator].ADD trialOperation.operand = newTrial
C#
Trial trial = new Trial() { draftId = draftId, baseCampaignId = baseCampaignId, name = "Test Trial #" + ExampleUtilities.GetRandomString(), trafficSplitPercent = 50 }; TrialOperation trialOperation = new TrialOperation() { @operator = Operator.ADD, operand = trial };
PHP
$trialService = $adWordsServices->get($session, TrialService::class); $trialAsynErrorService = $adWordsServices->get($session, TrialAsyncErrorService::class); // Create a trial. $trial = new Trial(); $trial->setDraftId($draftId); $trial->setBaseCampaignId($baseCampaignId); $trial->setName('Test Trial #' . uniqid()); $trial->setTrafficSplitPercent(50); // Create a trial operation and add it to the operations list. $operations = []; $operation = new TrialOperation(); $operation->setOperand($trial); $operation->setOperator(Operator::ADD); $operations[] = $operation; // Create the trial on the server. $trial = $trialService->mutate($operations)->getValue()[0];
Perl
my $trial = Google::Ads::AdWords::v201802::Trial->new({ draftId => $draft_id, baseCampaignId => $base_campaign_id, name => sprintf("Test Trial #%s", uniqid()), trafficSplitPercent => 50, }); # Create operation. my $trial_operation = Google::Ads::AdWords::v201802::TrialOperation->new({ operator => "ADD", operand => $trial }); # Add trial. my $result = $client->TrialService()->mutate({operations => [$trial_operation]});
Python
trial_service = client.GetService('TrialService', version='v201802') trial_async_error_service = client.GetService('TrialAsyncErrorService', version='v201802') trial = { 'draftId': draft_id, 'baseCampaignId': base_campaign_id, 'name': 'Test Trial #%d' % uuid.uuid4(), 'trafficSplitPercent': 50 } trial_operation = {'operator': 'ADD', 'operand': trial} trial_id = trial_service.mutate([trial_operation])['value'][0]['id']
Ruby
trial_srv = adwords.service(:TrialService, API_VERSION) trial_async_error_srv = adwords.service(:TrialAsyncErrorService, API_VERSION) trial = { :draft_id => draft_id, :base_campaign_id => base_campaign_id, :name => 'Test Trial #%d' % (Time.new.to_f * 1000).to_i, :traffic_split_percent => 50 } trial_operation = {:operator => 'ADD', :operand => trial} trial_result = trial_srv.mutate([trial_operation]) trial_id = trial_result[:value].first[:id]
Assim como na criação de rascunhos, anote o trialCampaignId
do teste para uso em operações futuras.
A criação de um teste é uma operação assíncrona (ao contrário da criação de rascunho). Um novo teste tem o status CREATING
. Pesquise até ele ser alterado para outro status antes de continuar:
Java
Selector trialSelector = new SelectorBuilder() .fields( TrialField.Id, TrialField.Status, TrialField.BaseCampaignId, TrialField.TrialCampaignId) .equalsId(trialId) .build(); trial = null; boolean isPending = true; int pollAttempts = 0; do { long sleepSeconds = (long) Math.scalb(30d, pollAttempts); System.out.printf("Sleeping for %d seconds.%n", sleepSeconds); Thread.sleep(sleepSeconds * 1000); trial = trialService.get(trialSelector).getEntries(0); System.out.printf("Trial ID %d has status '%s'.%n", trial.getId(), trial.getStatus()); pollAttempts++; isPending = TrialStatus.CREATING.equals(trial.getStatus()); } while (isPending && pollAttempts < MAX_POLL_ATTEMPTS);
VB
' Since creating a trial is asynchronous, we have to poll it to wait ' for it to finish. Dim trialSelector As New Selector() trialSelector.fields = New String() { Trial.Fields.Id, Trial.Fields.Status, Trial.Fields.BaseCampaignId, Trial.Fields.TrialCampaignId } trialSelector.predicates = New Predicate() { Predicate.Equals(Trial.Fields.Id, trialId) } newTrial = Nothing Dim isPending As Boolean = True Dim pollAttempts As Integer = 0 Do Dim sleepMillis As Integer = CType(Math.Pow(2, pollAttempts) * POLL_INTERVAL_SECONDS_BASE * 1000, Integer) Console.WriteLine("Sleeping {0} millis...", sleepMillis) Thread.Sleep(sleepMillis) newTrial = trialService.get(trialSelector).entries(0) Console.WriteLine("Trial ID {0} has status '{1}'.", newTrial.id, newTrial.status) pollAttempts = pollAttempts + 1 isPending = (newTrial.status = TrialStatus.CREATING) Loop While isPending AndAlso (pollAttempts <= MAX_RETRIES) If newTrial.status = TrialStatus.ACTIVE Then ' The trial creation was successful. Console.WriteLine("Trial created with ID {0} and trial campaign ID {1}.", newTrial.id, newTrial.trialCampaignId) ElseIf newTrial.status = TrialStatus.CREATION_FAILED Then ' The trial creation failed, and errors can be fetched from the ' TrialAsyncErrorService. Dim errorsSelector As New Selector() errorsSelector.fields = New String() { TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError } errorsSelector.predicates = New Predicate() { Predicate.Equals(TrialAsyncError.Fields.TrialId, newTrial.id) } Dim trialAsyncErrorPage As TrialAsyncErrorPage = trialAsyncErrorService.get( errorsSelector) If trialAsyncErrorPage.entries Is Nothing OrElse trialAsyncErrorPage.entries.Length = 0 Then Console.WriteLine("Could not retrieve errors for trial {0}.", newTrial.id) Else Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " & "following errors:", trialId, draftId) Dim i As Integer = 1 For Each err As TrialAsyncError In trialAsyncErrorPage.entries Dim asyncError As ApiError = err.asyncError Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', " & "trigger='{3}', fieldPath='{4}'", i, asyncError.ApiErrorType, asyncError.errorString, asyncError.trigger, asyncError.fieldPath) i += 1 Next End If Else ' Most likely, the trial is still being created. You can continue ' polling, but we have limited the number of attempts in the ' example. Console.WriteLine("Timed out waiting to create trial from draft ID {0} with " + "base campaign ID {1}.", draftId, baseCampaignId) End If
C#
// Since creating a trial is asynchronous, we have to poll it to wait // for it to finish. Selector trialSelector = new Selector() { fields = new string[] { Trial.Fields.Id, Trial.Fields.Status, Trial.Fields.BaseCampaignId, Trial.Fields.TrialCampaignId }, predicates = new Predicate[] { Predicate.Equals(Trial.Fields.Id, trialId) } }; trial = null; bool isPending = true; int pollAttempts = 0; do { int sleepMillis = (int) Math.Pow(2, pollAttempts) * POLL_INTERVAL_SECONDS_BASE * 1000; Console.WriteLine("Sleeping {0} millis...", sleepMillis); Thread.Sleep(sleepMillis); trial = trialService.get(trialSelector).entries[0]; Console.WriteLine("Trial ID {0} has status '{1}'.", trial.id, trial.status); pollAttempts++; isPending = (trial.status == TrialStatus.CREATING); } while (isPending && pollAttempts <= MAX_RETRIES); if (trial.status == TrialStatus.ACTIVE) { // The trial creation was successful. Console.WriteLine("Trial created with ID {0} and trial campaign ID {1}.", trial.id, trial.trialCampaignId); } else if (trial.status == TrialStatus.CREATION_FAILED) { // The trial creation failed, and errors can be fetched from the // TrialAsyncErrorService. Selector errorsSelector = new Selector() { fields = new string[] { TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError }, predicates = new Predicate[] { Predicate.Equals(TrialAsyncError.Fields.TrialId, trial.id) } }; TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector); if (trialAsyncErrorPage.entries == null || trialAsyncErrorPage.entries.Length == 0) { Console.WriteLine("Could not retrieve errors for trial {0}.", trial.id); } else { Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " + "following errors:", trial.id, draftId); int i = 0; foreach (TrialAsyncError error in trialAsyncErrorPage.entries) { ApiError asyncError = error.asyncError; Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', " + "trigger='{3}', fieldPath='{4}'", i++, asyncError.ApiErrorType, asyncError.errorString, asyncError.trigger, asyncError.fieldPath); } } } else { // Most likely, the trial is still being created. You can continue // polling, but we have limited the number of attempts in the // example. Console.WriteLine("Timed out waiting to create trial from draft ID {0} with " + "base campaign ID {1}.", draftId, baseCampaignId); }
PHP
$selector = new Selector(); $selector->setFields( ['Id', 'Status', 'BaseCampaignId', 'TrialCampaignId'] ); $selector->setPredicates( [new Predicate('Id', PredicateOperator::IN, [$trial->getId()])] ); // Since creating a trial is asynchronous, we have to poll it to wait for it // to finish. $pollAttempts = 0; $isPending = true; $trial = null; do { $sleepSeconds = self::POLL_FREQUENCY_SECONDS * pow(2, $pollAttempts); printf("Sleeping %d seconds...\n", $sleepSeconds); sleep($sleepSeconds); $trial = $trialService->get($selector)->getEntries()[0]; printf( "Trial ID %d has status '%s'.\n", $trial->getId(), $trial->getStatus() ); $pollAttempts++; $isPending = ($trial->getStatus() === TrialStatus::CREATING) ? true : false; } while ($isPending && $pollAttempts <= self::MAX_POLL_ATTEMPTS);
Perl
my $trial_id = $result->get_value()->[0]->get_id()->get_value(); my $predicate = Google::Ads::AdWords::v201802::Predicate->new({ field => "Id", operator => "IN", values => [$trial_id]}); my $paging = Google::Ads::AdWords::v201802::Paging->new({ startIndex => 0, numberResults => 1 }); my $selector = Google::Ads::AdWords::v201802::Selector->new({ fields => ["Id", "Status", "BaseCampaignId", "TrialCampaignId"], predicates => [$predicate], paging => $paging }); # Since creating a trial is asynchronous, we have to poll it to wait for # it to finish. my $poll_attempts = 0; my $is_pending = 1; my $end_time = time + JOB_TIMEOUT_IN_MILLISECONDS; do { # Check to see if the trial is still in the process of being created. my $result = $client->TrialService()->get({selector => $selector}); $trial = $result->get_entries()->[0]; my $waittime_in_milliseconds = JOB_BASE_WAITTIME_IN_MILLISECONDS * (2**$poll_attempts); if (((time + $waittime_in_milliseconds) < $end_time) and $trial->get_status() eq 'CREATING') { printf("Sleeping %d milliseconds...\n", $waittime_in_milliseconds); sleep($waittime_in_milliseconds / 1000); # Convert to seconds. $poll_attempts++; } } while (time < $end_time and $trial->get_status() eq 'CREATING');
Python
selector = { 'fields': ['Id', 'Status', 'BaseCampaignId', 'TrialCampaignId'], 'predicates': [{ 'field': 'Id', 'operator': 'IN', 'values': [trial_id] }] } # Since creating a trial is asynchronous, we have to poll it to wait for it to # finish. poll_attempts = 0 is_pending = True trial = None while is_pending and poll_attempts < MAX_POLL_ATTEMPTS: trial = trial_service.get(selector)['entries'][0] print 'Trial ID %d has status "%s"' % (trial['id'], trial['status']) poll_attempts += 1 is_pending = trial['status'] == 'CREATING' if is_pending: sleep_seconds = 30 * (2 ** poll_attempts) print 'Sleeping for %d seconds.' % sleep_seconds time.sleep(sleep_seconds)
Ruby
selector = { :fields => ['Id', 'Status', 'BaseCampaignId', 'TrialCampaignId'], :predicates => [ :field => 'Id', :operator => 'IN', :values => [trial_id] ] } poll_attempts = 0 is_pending = true trial = nil begin sleep_seconds = 30 * (2 ** poll_attempts) puts "Sleeping for %d seconds" % sleep_seconds sleep(sleep_seconds) trial = trial_srv.get(selector)[:entries].first puts "Trial ID %d has status '%s'" % [trial[:id], trial[:status]] poll_attempts += 1 is_pending = (trial[:status] == 'CREATING') end while is_pending and poll_attempts < MAX_POLL_ATTEMPTS
Depois de criar um teste, a campanha de teste associada funcionará basicamente como uma campanha normal. É possível ajustar a exibição do teste, exceto para os seguintes campos, que são ditados pelo teste e não podem ser alterados na campanha de teste (ao contrário de uma campanha normal):
status
name
startDate
endDate
budget
Tentar modificar um desses campos na campanha de teste resultará em um erro CampaignError.CANNOT_MODIFY_FOR_TRIAL_CAMPAIGN
. Você ainda pode alterar a maioria desses valores ao modificá-los em outro lugar e fazer com que se propaguem até a campanha de teste.
Ao criar um teste, se a startDate
e a endDate
não forem especificadas, elas serão definidas por padrão como as datas da campanha base. Modificações em startDate
, endDate
e name
do teste serão propagadas na campanha de teste. Modificações no status
da campanha base também serão propagadas nesse tipo de campanha. O orçamento é compartilhado com a campanha base e não pode ser modificado.
Operações de teste
Testes têm duas operações assíncronas separadas: criação e promoção. Se ocorrer um erro ao tentar criar ou promover um teste, será possível ver detalhes sobre erros específicos encontrados fornecendo o código do teste em TrialAsyncErrorService. Consulte a seção Erros para ver um exemplo.
Também é possível interromper um teste imediatamente alterando o status para HALTED
. Não é possível desfazer a interrupção de um teste para que ele comece a ser exibido junto à campanha base novamente, mas é possível promover, graduar ou arquivar um teste interrompido.
Campanhas de teste sempre compartilham um orçamento implicitamente com sua campanha base.
Esse orçamento não pode ser compartilhado com outras campanhas e precisa ser marcado especificamente como não compartilhado. Para isso, defina isExplicitlyShared
como falso.
Durante um teste, os leilões e o orçamento são compartilhados entre as campanhas base e de teste, cuja proporção é determinada pela porcentagem de divisão de tráfego que foi configurada para o teste. Se uma delas exceder o orçamento antecipadamente, a outra continua a funcionar e é incluída na porcentagem de leilões dela até que também esgote o próprio orçamento ou o teste termine. Após o fim do teste, a campanha base retoma todos os leilões e recebe 100% do orçamento.
Campanhas de teste são identificáveis usando o campaignTrialType
, que será TRIAL
. Assim como nos rascunhos, o baseCampaignId
apontará para a campanha base da qual o teste é copiado.
Promoção, graduação e arquivamento
Quando decidir se gosta dos resultados do teste, você terá algumas opções ao definir diferentes status do teste.
Se você não gostou no desempenho do teste, pode arquivá-lo definindo o status ARCHIVED
. Isso imediatamente marca a campanha de teste como REMOVED
e interrompe a experiência.
Por outro lado, se você gostou do desempenho do teste e ele ainda está ACTIVE
, é possível implementar a campanha de teste permanentemente de duas formas. Uma delas é aplicar todas as modificações de volta na campanha base, chamado de promoção. A outra é permitir a existência da campanha de teste de forma independente do próprio teste, possibilitando que todos os campos normalmente imutáveis durante um teste sejam modificados novamente e que funcione como uma campanha completa. Esse último processo é conhecido como graduação.
A promoção é um processo assíncrono, semelhante à promoção de um rascunho. Para começar, defina o status do teste como PROMOTING
e pesquise-o até ele ser alterado para PROMOTED
ou PROMOTE_FAILED
. É possível verificar erros assíncronos com o TrialAsyncErrorService.
A graduação é um processo síncrono. Ao definir o teste como GRADUATED
, a campanha estará imediatamente pronta para ser modificada e exibida. Ao graduar uma campanha de teste, também é necessário especificar um budgetId
para um novo orçamento a ser usado pela campanha. Ela não pode continuar compartilhando o orçamento da campanha base depois da graduação.
Relatórios
A campanha de teste não copia estatísticas anteriores da sua campanha base. Ela começa do zero. Durante a exibição do teste, as estatísticas da campanha base e da campanha de teste são auferidas separadamente. Cada uma tem suas próprias impressões, cliques etc. Isso não muda na promoção e na graduação. As estatísticas permanecem onde estão e jamais são copiadas em uma entidade diferente.
Depois da promoção, a campanha base mantém todas as suas estatísticas antigas e segue com novas alterações copiadas na campanha base. As estatísticas da campanha de teste depois da promoção continuam nela.
Depois da graduação, a campanha base e a campanha de teste continuam existindo como entidades separadas, e cada uma delas mantém suas próprias estatísticas para relatórios.
É possível usar relatórios normais, como o Relatório de desempenho de campanhas, nessas entidades. O campo baseCampaignId
nas campanhas representa a campanha base, e o campo campaignTrialType
permite distinguir entre campanhas normais e de teste.
Erros
Tanto rascunhos quanto testes permitem determinados tipos de ação (promoção para rascunhos, e criação e promoção para testes) assíncrona. Nesses casos, é necessário pesquisar o serviço recuando exponencialmente até que a operação seja concluída ou haja um erro. Se houver um erro, o status da entidade o indicará, e você poderá consultar o AsyncErrorService adequado para ver detalhes.
Por exemplo, se houver falha na promoção de um teste, será possível solicitar detalhes sobre os erros, desta forma:
Java
Selector errorsSelector = new SelectorBuilder() .fields(TrialAsyncErrorField.TrialId, TrialAsyncErrorField.AsyncError) .equals(TrialAsyncErrorField.TrialId, trial.getId().toString()) .build(); TrialAsyncErrorServiceInterface trialAsyncErrorService = adWordsServices.get(session, TrialAsyncErrorServiceInterface.class); TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector); if (trialAsyncErrorPage.getEntries() == null || trialAsyncErrorPage.getEntries().length == 0) { System.out.printf( "Could not retrieve errors for trial ID %d for draft ID %d.%n", trial.getId(), draftId); } else { System.out.printf( "Could not create trial ID %d for draft ID %d due to the following errors:%n", trial.getId(), draftId); int i = 0; for (TrialAsyncError error : trialAsyncErrorPage.getEntries()) { ApiError asyncError = error.getAsyncError(); System.out.printf( "Error #%d: errorType='%s', errorString='%s', trigger='%s', fieldPath='%s'%n", i++, asyncError.getApiErrorType(), asyncError.getErrorString(), asyncError.getTrigger(), asyncError.getFieldPath()); }
VB
ElseIf newTrial.status = TrialStatus.CREATION_FAILED Then ' The trial creation failed, and errors can be fetched from the ' TrialAsyncErrorService. Dim errorsSelector As New Selector() errorsSelector.fields = New String() { TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError } errorsSelector.predicates = New Predicate() { Predicate.Equals(TrialAsyncError.Fields.TrialId, newTrial.id) } Dim trialAsyncErrorPage As TrialAsyncErrorPage = trialAsyncErrorService.get( errorsSelector) If trialAsyncErrorPage.entries Is Nothing OrElse trialAsyncErrorPage.entries.Length = 0 Then Console.WriteLine("Could not retrieve errors for trial {0}.", newTrial.id) Else Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " & "following errors:", trialId, draftId) Dim i As Integer = 1 For Each err As TrialAsyncError In trialAsyncErrorPage.entries Dim asyncError As ApiError = err.asyncError Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', " & "trigger='{3}', fieldPath='{4}'", i, asyncError.ApiErrorType, asyncError.errorString, asyncError.trigger, asyncError.fieldPath) i += 1 Next End If
C#
} else if (trial.status == TrialStatus.CREATION_FAILED) { // The trial creation failed, and errors can be fetched from the // TrialAsyncErrorService. Selector errorsSelector = new Selector() { fields = new string[] { TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError }, predicates = new Predicate[] { Predicate.Equals(TrialAsyncError.Fields.TrialId, trial.id) } }; TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector); if (trialAsyncErrorPage.entries == null || trialAsyncErrorPage.entries.Length == 0) { Console.WriteLine("Could not retrieve errors for trial {0}.", trial.id); } else { Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " + "following errors:", trial.id, draftId); int i = 0; foreach (TrialAsyncError error in trialAsyncErrorPage.entries) { ApiError asyncError = error.asyncError; Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', " + "trigger='{3}', fieldPath='{4}'", i++, asyncError.ApiErrorType, asyncError.errorString, asyncError.trigger, asyncError.fieldPath); } }
PHP
$selector = new Selector(); $selector->setFields(['TrialId', 'AsyncError']); $selector->setPredicates( [new Predicate('TrialId', PredicateOperator::IN, [$trial->getId()])] ); $errors = $trialAsynErrorService->get($selector)->getEntries(); if (count($errors) === 0) { printf( "Could not retrieve errors for the trial with ID %d\n", $trial->getId() ); } else { printf("Could not create trial due to the following errors:\n"); $i = 0; foreach ($errors as $error) { printf("Error #%d: %s\n", $i++, $error->getAsyncError()); } }
Perl
my $error_selector = Google::Ads::AdWords::v201802::Selector->new({ fields => ["TrialId", "AsyncError"], predicates => [ Google::Ads::AdWords::v201802::Predicate->new({ field => "TrialId", operator => "IN", values => [$trial_id]})]}); my $errors = $client->TrialAsyncErrorService->get({selector => $error_selector}) ->get_entries(); if (!$errors) { printf("Could not retrieve errors for trial %d", $trial->get_id()); } else { printf("Could not create trial due to the following errors:"); my $index = 0; for my $error ($errors) { printf("Error %d: %s", $index, $error->get_asyncError() ->get_errorString()); $index++; } }
Python
selector = { 'fields': ['TrialId', 'AsyncError'], 'predicates': [{ 'field': 'TrialId', 'operator': 'IN', 'values': [trial['id']] }] } errors = trial_async_error_service.get(selector)['entries'] if not errors: print 'Could not retrieve errors for trial %d' % trial['id'] else: print 'Could not create trial due to the following errors:' for error in errors: print 'Error: %s' % error['asyncError']
Ruby
selector = { :fields => ['TrialId', 'AsyncError'], :predicates => [ {:field => 'TrialId', :operator => 'IN', :values => [trial[:id]]} ] } errors = trial_async_error_srv.get(selector)[:entries] if errors.nil? puts "Could not retrieve errors for trial %d" % trial[:id] else puts "Could not create trial due to the following errors:" errors.each_with_index do |error, i| puts "Error #%d: %s" % [i, error[:async_error]] end end
Operações assíncronas normalmente continuam aplicando as alterações mesmo depois de encontrar um erro. Portanto, erros registrados poderão aumentar rapidamente se houver um número significativo de modificações incompatíveis. Possíveis causas incluem nomes duplicados de grupos de anúncios, estratégias de lance incompatíveis ou limites de conta ultrapassados.