Skip to content

refactor(campaigns): extract shared helpers to _campaigns_base.py (#602, step 1)#612

Merged
axisrow merged 1 commit into
mainfrom
ao/issue-602-campaigns
Jul 23, 2026
Merged

refactor(campaigns): extract shared helpers to _campaigns_base.py (#602, step 1)#612
axisrow merged 1 commit into
mainfrom
ao/issue-602-campaigns

Conversation

@axisrow

@axisrow axisrow commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

First step of an incremental split of the 7 209-line campaigns.py monolith (issue #602). This PR is Step 1 only — extracting shared helpers while keeping the CLI surface byte-for-byte identical. Per-campaign-type logic (text, unified, dynamic, smart, mobile_app, cpm_banner) follows in subsequent PRs.

Following the architecture decision to split incrementally rather than swallow the whole file in one PR (each campaign type one at a time, verified green after each).

What moved

All campaign-type-agnostic code moved to a new sibling module direct_cli/commands/_campaigns_base.py:

  • ConstantsCAMPAIGNS_GET_CRITERIA_LIMITS, YES_NO, ATTRIBUTION_MODELS, RELEVANT_KEYWORDS_MODES, VIDEO_TARGETS, CLIENT_INFO_MAX_LENGTH, BLOCKED_IPS_MAX_ITEMS, EXCLUDED_SITES_MAX_ITEMS, EXCLUDED_SITE_MAX_LENGTH, TIME_TARGETING_SCHEDULE_MAX_ITEMS, NEGATIVE_KEYWORD_SHARED_SET_IDS_MAX_ITEMS, HH_MM_RE, SMS_EVENTS
  • Typed-flag validators_validate_max_length, _validate_sms_time, _upper_yes_no, _time_targeting_schedule_option, _parse_csv_option, _deprecated_campaigns_structured_option
  • Payload builders_build_notification, _build_time_targeting, _build_relevant_keywords, _build_dynamic_placement_types, _build_frequency_cap, _build_package_bidding_strategy, _build_smart_package_bidding_strategy, _priority_goals_update_items, _route_cpa_flag
  • Array option helpers_array_of_string_option, _array_of_integer_option
  • Reusable TextCampaign strategy click.option groups_TEXT_SEARCH_STRATEGY_OPTIONS, _TEXT_NETWORK_STRATEGY_OPTIONS and their *_UPDATE variants, plus the _apply_options helper and the four _text_*_strategy_options[_update] decorators

campaigns.py re-imports every name it still references, so nothing in the command surface changed.

CLI surface

No breaking changes. Every command (get/add/update/lifecycle) and every flag is unchanged. This is a pure internal relocation — no legacy aliases, strict WSDL parity preserved.

Tests run

  • pytest (full offline tier): 2507 passed, 8 skipped (identical to baseline)
  • pytest tests/test_wsdl_parity_gate.py tests/test_api_coverage.py: 263 passed, 6 skipped (green)
  • pytest tests/test_dry_run.py: 1395 passed
  • pytest tests/test_comprehensive.py: 21 passed
  • black direct_cli tests — my files clean (the 16 reformatted files are pre-existing _vendor/test files, unchanged by this PR)
  • flake8 direct_cli/commands/campaigns.py direct_cli/commands/_campaigns_base.py — clean (only pre-existing E501 long-line warnings that also exist on main)

Risks / follow-ups

Part of #602.

🤖 Generated with Claude Code

…, step 1)

First step of an incremental split of the 7 209-line campaigns.py monolith
(issue #602). Moves all campaign-type-agnostic code into a new sibling module
direct_cli/commands/_campaigns_base.py:

- constants (CAMPAIGNS_GET_CRITERIA_LIMITS, YES_NO, ATTRIBUTION_MODELS, ...)
- typed-flag validators (_validate_max_length, _validate_sms_time)
- payload builders (_build_notification, _build_time_targeting,
  _build_relevant_keywords, _build_dynamic_placement_types,
  _build_frequency_cap, _build_package_bidding_strategy,
  _build_smart_package_bidding_strategy, _priority_goals_update_items,
  _route_cpa_flag)
- reusable TextCampaign strategy click.option groups
  (_TEXT_*_STRATEGY_OPTIONS and their *_UPDATE variants) + _apply_options

campaigns.py re-imports every name, so the CLI surface, every flag and every
--dry-run payload is byte-for-byte identical (offline tier 2507 passed,
test_wsdl_parity_gate + test_api_coverage green). Per-campaign-type logic
(text/unified/dynamic/smart/mobile_app/cpm_banner) follows in subsequent PRs.

Closes part of #602.

Co-Authored-By: Claude <noreply@anthropic.com>
@axisrow
axisrow merged commit 931e7ed into main Jul 23, 2026
6 checks passed
axisrow added a commit that referenced this pull request Jul 23, 2026
…nner.py (#613)

Step 2 of the incremental campaigns.py split (epic #602, follows shared-helpers
extraction in PR #612). Moves the CPM_BANNER_CAMPAIGN add/update subtype-block
composition out of the inline `elif campaign_type_norm == "CPM_BANNER_CAMPAIGN":`
branches into a new sibling module direct_cli/commands/_campaigns_cpm_banner.py
(build_add_block / build_update_block).

Both commands now snapshot their CLI parameters once via `p = dict(locals())`
and delegate; the per-type builder pulls only the CPM-relevant flags from `p`.
CLI surface, every flag and every --dry-run payload is byte-for-byte identical
(28 test_campaigns_*cpm* fixtures green, offline tier 2507 passed, WSDL parity +
API coverage green).

Smallest campaign type, used to validate the per-type extraction mechanics
before the larger types (mobile_app, smart, dynamic, unified, text).

Part of #602.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

epic(campaigns): split campaigns.py (7 209 lines) into modular components

1 participant