Skip to content

feat: Add daypart targeting support #1031

Description

@bokelley

Summary

Support daypart targeting in TargetingOverlay - allowing buyers to restrict ad delivery to specific time windows (e.g., "weekday mornings" or "prime time").

AdCP version: v3.0.0-beta.3 (adcp library 3.3.0)

AdCP v3 Daypart Format

class DaypartTarget(AdCPBaseModel):
    days: list[DayOfWeek]   # e.g., ["monday", "tuesday", "wednesday", "thursday", "friday"]
    start_hour: int          # Inclusive, 0-23
    end_hour: int            # Exclusive, 1-24
    label: str | None        # e.g., "Morning Drive", "Prime Time"

# On TargetingOverlay:
class TargetingOverlay(AdCPBaseModel):
    daypart_targets: list[DaypartTarget] | None

Examples:

  • Morning drive: {days: [mon-fri], start_hour: 6, end_hour: 10, label: "Morning Drive"}
  • Prime time: {days: [mon-sun], start_hour: 20, end_hour: 23, label: "Prime Time"}
  • Weekend only: {days: [sat, sun], start_hour: 0, end_hour: 24}

Current State

The internal Targeting schema (src/core/schemas.py) has no daypart support. The GAM targeting manager (src/adapters/gam/managers/targeting.py) doesn't handle dayparting.

Implementation Plan

1. Update internal Targeting schema

# src/core/schemas.py
class DaypartTarget(BaseModel):
    days: list[str]
    start_hour: int
    end_hour: int
    label: str | None = None

class Targeting:
    # ... existing fields ...
    daypart_targets: list[DaypartTarget] | None = None

2. Convert from AdCP format

In the targeting overlay → internal targeting conversion (used in create_media_buy and update_media_buy), map TargetingOverlay.daypart_targets to internal format.

3. GAM adapter: DayPartTargeting

GAM supports dayparting via DayPartTargeting on line items:

# GAM API structure
daypart_targeting = {
    'dayParts': [{
        'dayOfWeek': 'MONDAY',       # MONDAY-SUNDAY
        'startTime': {'hour': 6, 'minute': 0},
        'endTime': {'hour': 10, 'minute': 0},
    }]
}

Mapping:

  • AdCP days → GAM dayOfWeek (uppercase)
  • AdCP start_hour → GAM startTime.hour (minute always 0)
  • AdCP end_hour → GAM endTime.hour (minute always 0)

4. Update capabilities

# src/core/tools/capabilities.py
# Advertise daypart support in capabilities response
targeting_capabilities.daypart = True  # or structured capability

Files to Modify

File Change
src/core/schemas.py Add DaypartTarget to internal Targeting
src/core/tools/media_buy_create.py Handle daypart in targeting conversion
src/core/tools/media_buy_update.py Handle daypart in targeting conversion
src/adapters/gam/managers/targeting.py Translate to GAM DayPartTargeting
src/adapters/mock/adapter.py Store/validate daypart targeting
src/core/tools/capabilities.py Advertise daypart capability
tests/unit/test_targeting.py Test daypart conversion

Testing

uv run pytest tests/unit/test_targeting.py -v

Test cases:

  • Single daypart (weekday mornings) converts to GAM format
  • Multiple dayparts (morning + evening) apply correctly
  • Full day (start_hour=0, end_hour=24) works
  • Invalid hours rejected (start >= end, hour > 24)
  • Capabilities correctly advertise daypart support
  • Daypart persists through create → update → delivery cycle

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status
    Ready for Dev

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions