|
@@ -105,6 +105,7 @@ def target_for_ad(
|
|
|
next_paused = False
|
|
next_paused = False
|
|
|
pause_reason = None
|
|
pause_reason = None
|
|
|
limit_reached = False
|
|
limit_reached = False
|
|
|
|
|
+ defer_to_next_day = False
|
|
|
|
|
|
|
|
if manual_suspend:
|
|
if manual_suspend:
|
|
|
if decision not in (DECISION_PAUSE, DECISION_CUTOFF):
|
|
if decision not in (DECISION_PAUSE, DECISION_CUTOFF):
|
|
@@ -118,6 +119,7 @@ def target_for_ad(
|
|
|
"boost_limit_reached": False,
|
|
"boost_limit_reached": False,
|
|
|
"bid_change_needed": current_bid != base_bid,
|
|
"bid_change_needed": current_bid != base_bid,
|
|
|
"status_change_needed": False,
|
|
"status_change_needed": False,
|
|
|
|
|
+ "defer_to_next_day": False,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if decision == DECISION_BOOST:
|
|
if decision == DECISION_BOOST:
|
|
@@ -134,10 +136,14 @@ def target_for_ad(
|
|
|
elif decision == DECISION_RESTORE:
|
|
elif decision == DECISION_RESTORE:
|
|
|
if managed_suspend:
|
|
if managed_suspend:
|
|
|
target_status = ACTIVE_STATUS
|
|
target_status = ACTIVE_STATUS
|
|
|
- elif decision in (DECISION_PAUSE, DECISION_CUTOFF):
|
|
|
|
|
|
|
+ elif decision == DECISION_PAUSE:
|
|
|
|
|
+ if managed_suspend:
|
|
|
|
|
+ target_status = ACTIVE_STATUS
|
|
|
|
|
+ defer_to_next_day = True
|
|
|
|
|
+ elif decision == DECISION_CUTOFF:
|
|
|
target_status = SUSPEND_STATUS if current_status != SUSPEND_STATUS else None
|
|
target_status = SUSPEND_STATUS if current_status != SUSPEND_STATUS else None
|
|
|
next_paused = True
|
|
next_paused = True
|
|
|
- pause_reason = "low_cpm" if decision == DECISION_PAUSE else "cutoff"
|
|
|
|
|
|
|
+ pause_reason = "cutoff"
|
|
|
else:
|
|
else:
|
|
|
raise ValueError(f"Unsupported decision: {decision}")
|
|
raise ValueError(f"Unsupported decision: {decision}")
|
|
|
|
|
|
|
@@ -151,6 +157,7 @@ def target_for_ad(
|
|
|
"bid_change_needed": current_bid != target_bid,
|
|
"bid_change_needed": current_bid != target_bid,
|
|
|
"status_change_needed": target_status is not None
|
|
"status_change_needed": target_status is not None
|
|
|
and target_status != current_status,
|
|
and target_status != current_status,
|
|
|
|
|
+ "defer_to_next_day": defer_to_next_day,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -302,7 +309,23 @@ def execute_inventory_action(
|
|
|
if plan is None:
|
|
if plan is None:
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
- needs_api = plan["bid_change_needed"] or plan["status_change_needed"]
|
|
|
|
|
|
|
+ before_begin_date = str(ad.get("begin_date") or "")
|
|
|
|
|
+ target_begin_date = None
|
|
|
|
|
+ begin_date_change_needed = False
|
|
|
|
|
+ if plan["defer_to_next_day"]:
|
|
|
|
|
+ next_day = (now.date() + timedelta(days=1)).isoformat()
|
|
|
|
|
+ target_begin_date = (
|
|
|
|
|
+ before_begin_date
|
|
|
|
|
+ if before_begin_date > next_day
|
|
|
|
|
+ else next_day
|
|
|
|
|
+ )
|
|
|
|
|
+ begin_date_change_needed = before_begin_date != target_begin_date
|
|
|
|
|
+
|
|
|
|
|
+ needs_api = (
|
|
|
|
|
+ plan["bid_change_needed"]
|
|
|
|
|
+ or plan["status_change_needed"]
|
|
|
|
|
+ or begin_date_change_needed
|
|
|
|
|
+ )
|
|
|
execution_status = "planned" if needs_api else "noop"
|
|
execution_status = "planned" if needs_api else "noop"
|
|
|
error = None
|
|
error = None
|
|
|
if apply and state is None:
|
|
if apply and state is None:
|
|
@@ -313,8 +336,7 @@ def execute_inventory_action(
|
|
|
bid_field=bid_field,
|
|
bid_field=bid_field,
|
|
|
base_bid_fen=base_bid,
|
|
base_bid_fen=base_bid,
|
|
|
boosted_date=None,
|
|
boosted_date=None,
|
|
|
- paused_by_strategy=decision
|
|
|
|
|
- in (DECISION_PAUSE, DECISION_CUTOFF),
|
|
|
|
|
|
|
+ paused_by_strategy=plan["paused_by_strategy"],
|
|
|
pause_reason=plan["pause_reason"],
|
|
pause_reason=plan["pause_reason"],
|
|
|
last_action="REGISTER_BASE",
|
|
last_action="REGISTER_BASE",
|
|
|
action_at=now,
|
|
action_at=now,
|
|
@@ -338,19 +360,26 @@ def execute_inventory_action(
|
|
|
)
|
|
)
|
|
|
if apply and needs_api:
|
|
if apply and needs_api:
|
|
|
try:
|
|
try:
|
|
|
- tencent.update_ad(
|
|
|
|
|
- account_id,
|
|
|
|
|
- adgroup_id,
|
|
|
|
|
- bid_field=bid_field
|
|
|
|
|
- if plan["bid_change_needed"]
|
|
|
|
|
- else None,
|
|
|
|
|
- target_bid_fen=plan["target_bid_fen"]
|
|
|
|
|
- if plan["bid_change_needed"]
|
|
|
|
|
- else None,
|
|
|
|
|
- target_status=plan["target_status"]
|
|
|
|
|
- if plan["status_change_needed"]
|
|
|
|
|
- else None,
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ if begin_date_change_needed:
|
|
|
|
|
+ tencent.update_ad_begin_dates(
|
|
|
|
|
+ account_id,
|
|
|
|
|
+ [adgroup_id],
|
|
|
|
|
+ str(target_begin_date),
|
|
|
|
|
+ )
|
|
|
|
|
+ if plan["bid_change_needed"] or plan["status_change_needed"]:
|
|
|
|
|
+ tencent.update_ad(
|
|
|
|
|
+ account_id,
|
|
|
|
|
+ adgroup_id,
|
|
|
|
|
+ bid_field=bid_field
|
|
|
|
|
+ if plan["bid_change_needed"]
|
|
|
|
|
+ else None,
|
|
|
|
|
+ target_bid_fen=plan["target_bid_fen"]
|
|
|
|
|
+ if plan["bid_change_needed"]
|
|
|
|
|
+ else None,
|
|
|
|
|
+ target_status=plan["target_status"]
|
|
|
|
|
+ if plan["status_change_needed"]
|
|
|
|
|
+ else None,
|
|
|
|
|
+ )
|
|
|
execution_status = "success"
|
|
execution_status = "success"
|
|
|
api_updates += 1
|
|
api_updates += 1
|
|
|
time.sleep(0.15)
|
|
time.sleep(0.15)
|
|
@@ -374,6 +403,9 @@ def execute_inventory_action(
|
|
|
"boost_limit_reached": plan["boost_limit_reached"],
|
|
"boost_limit_reached": plan["boost_limit_reached"],
|
|
|
"bid_change_needed": plan["bid_change_needed"],
|
|
"bid_change_needed": plan["bid_change_needed"],
|
|
|
"status_change_needed": plan["status_change_needed"],
|
|
"status_change_needed": plan["status_change_needed"],
|
|
|
|
|
+ "before_begin_date": before_begin_date,
|
|
|
|
|
+ "target_begin_date": target_begin_date,
|
|
|
|
|
+ "begin_date_change_needed": begin_date_change_needed,
|
|
|
"status": execution_status,
|
|
"status": execution_status,
|
|
|
"error": error,
|
|
"error": error,
|
|
|
}
|
|
}
|
|
@@ -433,6 +465,7 @@ def execute_inventory_action(
|
|
|
continue
|
|
continue
|
|
|
if not (
|
|
if not (
|
|
|
result.get("bid_change_needed")
|
|
result.get("bid_change_needed")
|
|
|
|
|
+ or result.get("begin_date_change_needed")
|
|
|
or (
|
|
or (
|
|
|
result.get("decision") in (DECISION_PAUSE, DECISION_CUTOFF)
|
|
result.get("decision") in (DECISION_PAUSE, DECISION_CUTOFF)
|
|
|
and result.get("status_change_needed")
|
|
and result.get("status_change_needed")
|
|
@@ -547,7 +580,7 @@ def run_cycle(
|
|
|
execution = execute_inventory_action(
|
|
execution = execute_inventory_action(
|
|
|
run_id=run_id,
|
|
run_id=run_id,
|
|
|
now=now,
|
|
now=now,
|
|
|
- decision=DECISION_CUTOFF,
|
|
|
|
|
|
|
+ decision=DECISION_RESTORE,
|
|
|
observed_partition=None,
|
|
observed_partition=None,
|
|
|
observed_cpm=None,
|
|
observed_cpm=None,
|
|
|
apply=apply,
|
|
apply=apply,
|
|
@@ -589,7 +622,8 @@ def run_cycle(
|
|
|
last_inventory_refresh_at=now,
|
|
last_inventory_refresh_at=now,
|
|
|
last_evaluated_at=now,
|
|
last_evaluated_at=now,
|
|
|
)
|
|
)
|
|
|
- return payload
|
|
|
|
|
|
|
+ else:
|
|
|
|
|
+ return payload
|
|
|
|
|
|
|
|
if cpm_override is not None:
|
|
if cpm_override is not None:
|
|
|
signal = HourlyCpm(
|
|
signal = HourlyCpm(
|