Skip to content

Geo exclusions and ad schedule on draft_campaign and update_campaign - #121

Merged
kLOsk merged 2 commits into
kLOsk:mainfrom
illia-sapryga:feat/campaign-geo-exclude-schedule
Nov 12, 2026
Merged

kLOsk merged 2 commits into
kLOsk:mainfrom
illia-sapryga:feat/campaign-geo-exclude-schedule

Conversation

@illia-sapryga

@illia-sapryga illia-sapryga commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Today a new campaign needs follow-up plans for its exclusions and its schedule: add_negative_locations and add_ad_schedule, each previewed and applied after the campaign exists. There is also no way to say "this is the complete list" on an existing campaign. You have to work out the difference yourself and go through remove_entity one criterion at a time.

This adds geo_exclude_ids and ad_schedule to both tools, as parameters rather than new tools.

On draft_campaign they go in the campaign's atomic mutate, so a failure leaves nothing behind.

On update_campaign they replace the whole list: omitted leaves it, [] clears it. The preview shows current_geo_exclude_ids and current_ad_schedule, and apply re-reads them and refuses if they moved after the preview. Only the entries that differ are removed or added, so a window or exclusion that stays keeps its criterion and history. Exclusion removals go before the positive geo operations and exclusion creates after them, so a location can move between the two lists in one request. One request goes: campaign fields, exclusion removals, positive location removals and creates, languages, exclusion creates, then schedule removals before schedule creates; TestOneRequestOrder pins that order, including keyword_match_type (#117) in the same request.

Windows go through _validate_schedule, so the rules are the ones add_ad_schedule has. On update_campaign these need a double confirmation:

  • a first schedule, using add_ad_schedule's logic and warning text (now a shared helper)
  • clearing a schedule, because the campaign serves at all hours again
  • dropping a geo exclusion, because the campaign can serve there again

On draft_campaign a schedule only adds a warning: the campaign is created PAUSED, so nothing stops serving, and the preview says it will serve only inside the windows once it is enabled.

When geo_exclude_ids is passed next to geo_target_ids, the "negative exclusions will be preserved" warning no longer appears, since they are being replaced. A list with a blank entry is refused, so [" "] can't quietly clear the exclusions. Passing the values a campaign already has comes back as nothing to change.

What it does not do: add_negative_locations and add_ad_schedule stay as they are, for adding to what's there. An exclusion that overlaps geo_target_ids in the same call is refused, but an overlap with the campaign's existing positive targets is left to Google's answer in the dry run.

Tested with the full suite plus tests/test_campaign_geo_exclude_schedule.py. Plans run through confirm_and_apply(dry_run=True) with real protos and the real validate-only wrapper, and fake services that only record the request. GAQL reads are stubbed. I have not run this code against a live account, so Google's answer to the replacement requests is untested, including the removal order.

Rebased onto main (0.41.2), next to #117's keyword_match_type.

@kLOsk

kLOsk commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Thanks, the replace semantics and the re-check at apply look right. Answers and next steps:

  1. Schedule on draft_campaign: warning only, no double confirmation. The campaign is new and PAUSED, so nothing stops serving, and the dialog's "cannot be undone" wording would be wrong there. Keep the double confirmation on update_campaign.
  2. Rebase. update_campaign: campaign-level broad match #117 landed in update_campaign and conflicts with this in the same spots (signature, change check, apply, rules table row).
  3. Live check. Once it's rebased we'll run validate-only dry runs on a test account, mainly the removal order and a location moving between exclusions and targets in one request, then merge.

draft_campaign puts them in the campaign's atomic mutate. update_campaign
replaces the whole list: omitted leaves it, [] clears it. The preview shows the
current values and apply refuses if they changed after it; only the entries
that differ are removed or added.

Windows use add_ad_schedule's validation. A first schedule and clearing a
schedule need a double confirmation, as does dropping a geo exclusion.
The campaign is created PAUSED, so a schedule stops nothing from serving,
and the dialog's "cannot be undone" was wrong there. The preview now says
the campaign serves only inside the windows once it is enabled.
update_campaign keeps the double confirmation for a first schedule, for
clearing a schedule and for dropping an exclusion.

Tests pin the order of the one update_campaign request: campaign fields,
exclusion removes, positive geo removes and creates, languages, exclusion
creates, schedule removes, then schedule creates. A location moving from
the exclusions to the targets, or back, is removed before it is created
again, and keyword_match_type (kLOsk#117) can go in the same request.

The GA4 annotation suggested after an applied update_campaign now counts
exclusions as targeting and names a schedule change. The rules and tool
descriptions describe the final behavior.
@illia-sapryga
illia-sapryga force-pushed the feat/campaign-geo-exclude-schedule branch from 0a114a5 to 776754c Compare November 10, 2026 22:01
@illia-sapryga

Copy link
Copy Markdown
Contributor Author

Thanks, Daniel. Rebased onto main and added 776754c:

  • draft_campaign: a schedule now only warns. The campaign is created PAUSED, and the preview says it serves only inside the windows once it is enabled. update_campaign keeps the double confirmation for a first schedule, for clearing one and for dropping an exclusion.
  • update_campaign: campaign-level broad match #117: both sets of parameters sit side by side in the signature, the preview and the apply. The "nothing to change" check now runs after keyword_match_type is in the plan, so an update that only sets broad match isn't refused.
  • For your live check: one request goes campaign fields, exclusion removals, positive location removals and creates, languages, exclusion creates, then schedule removals before schedule creates. TestOneRequestOrder pins that, including a location moving each way between the lists and keyword_match_type in the same request.
  • The GA4 annotation suggestion now counts exclusions as targeting and names a schedule change.

The description is updated to match.

@kLOsk
kLOsk merged commit cdbb749 into kLOsk:main Nov 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants